9e39fb86ca80ef3682e6b2aaa6f9e8c2b526ddf2
[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 = (typeof(cfg.style) == 'undefined' ? this.style : 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     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311
7312 /**
7313  * @class Roo.grid.ColumnModel
7314  * @extends Roo.util.Observable
7315  * This is the default implementation of a ColumnModel used by the Grid. It defines
7316  * the columns in the grid.
7317  * <br>Usage:<br>
7318  <pre><code>
7319  var colModel = new Roo.grid.ColumnModel([
7320         {header: "Ticker", width: 60, sortable: true, locked: true},
7321         {header: "Company Name", width: 150, sortable: true},
7322         {header: "Market Cap.", width: 100, sortable: true},
7323         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7324         {header: "Employees", width: 100, sortable: true, resizable: false}
7325  ]);
7326  </code></pre>
7327  * <p>
7328  
7329  * The config options listed for this class are options which may appear in each
7330  * individual column definition.
7331  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7332  * @constructor
7333  * @param {Object} config An Array of column config objects. See this class's
7334  * config objects for details.
7335 */
7336 Roo.grid.ColumnModel = function(config){
7337         /**
7338      * The config passed into the constructor
7339      */
7340     this.config = []; //config;
7341     this.lookup = {};
7342
7343     // if no id, create one
7344     // if the column does not have a dataIndex mapping,
7345     // map it to the order it is in the config
7346     for(var i = 0, len = config.length; i < len; i++){
7347         this.addColumn(config[i]);
7348         
7349     }
7350
7351     /**
7352      * The width of columns which have no width specified (defaults to 100)
7353      * @type Number
7354      */
7355     this.defaultWidth = 100;
7356
7357     /**
7358      * Default sortable of columns which have no sortable specified (defaults to false)
7359      * @type Boolean
7360      */
7361     this.defaultSortable = false;
7362
7363     this.addEvents({
7364         /**
7365              * @event widthchange
7366              * Fires when the width of a column changes.
7367              * @param {ColumnModel} this
7368              * @param {Number} columnIndex The column index
7369              * @param {Number} newWidth The new width
7370              */
7371             "widthchange": true,
7372         /**
7373              * @event headerchange
7374              * Fires when the text of a header changes.
7375              * @param {ColumnModel} this
7376              * @param {Number} columnIndex The column index
7377              * @param {Number} newText The new header text
7378              */
7379             "headerchange": true,
7380         /**
7381              * @event hiddenchange
7382              * Fires when a column is hidden or "unhidden".
7383              * @param {ColumnModel} this
7384              * @param {Number} columnIndex The column index
7385              * @param {Boolean} hidden true if hidden, false otherwise
7386              */
7387             "hiddenchange": true,
7388             /**
7389          * @event columnmoved
7390          * Fires when a column is moved.
7391          * @param {ColumnModel} this
7392          * @param {Number} oldIndex
7393          * @param {Number} newIndex
7394          */
7395         "columnmoved" : true,
7396         /**
7397          * @event columlockchange
7398          * Fires when a column's locked state is changed
7399          * @param {ColumnModel} this
7400          * @param {Number} colIndex
7401          * @param {Boolean} locked true if locked
7402          */
7403         "columnlockchange" : true
7404     });
7405     Roo.grid.ColumnModel.superclass.constructor.call(this);
7406 };
7407 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7408     /**
7409      * @cfg {String} header The header text to display in the Grid view.
7410      */
7411     /**
7412      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7413      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7414      * specified, the column's index is used as an index into the Record's data Array.
7415      */
7416     /**
7417      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7418      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7419      */
7420     /**
7421      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7422      * Defaults to the value of the {@link #defaultSortable} property.
7423      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7424      */
7425     /**
7426      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7427      */
7428     /**
7429      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7430      */
7431     /**
7432      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7433      */
7434     /**
7435      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7436      */
7437     /**
7438      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7439      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7440      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7441      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7442      */
7443        /**
7444      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7445      */
7446     /**
7447      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7448      */
7449     /**
7450      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7451      */
7452     /**
7453      * @cfg {String} cursor (Optional)
7454      */
7455     /**
7456      * @cfg {String} tooltip (Optional)
7457      */
7458     /**
7459      * @cfg {Number} xs (Optional)
7460      */
7461     /**
7462      * @cfg {Number} sm (Optional)
7463      */
7464     /**
7465      * @cfg {Number} md (Optional)
7466      */
7467     /**
7468      * @cfg {Number} lg (Optional)
7469      */
7470     /**
7471      * Returns the id of the column at the specified index.
7472      * @param {Number} index The column index
7473      * @return {String} the id
7474      */
7475     getColumnId : function(index){
7476         return this.config[index].id;
7477     },
7478
7479     /**
7480      * Returns the column for a specified id.
7481      * @param {String} id The column id
7482      * @return {Object} the column
7483      */
7484     getColumnById : function(id){
7485         return this.lookup[id];
7486     },
7487
7488     
7489     /**
7490      * Returns the column Object for a specified dataIndex.
7491      * @param {String} dataIndex The column dataIndex
7492      * @return {Object|Boolean} the column or false if not found
7493      */
7494     getColumnByDataIndex: function(dataIndex){
7495         var index = this.findColumnIndex(dataIndex);
7496         return index > -1 ? this.config[index] : false;
7497     },
7498     
7499     /**
7500      * Returns the index for a specified column id.
7501      * @param {String} id The column id
7502      * @return {Number} the index, or -1 if not found
7503      */
7504     getIndexById : function(id){
7505         for(var i = 0, len = this.config.length; i < len; i++){
7506             if(this.config[i].id == id){
7507                 return i;
7508             }
7509         }
7510         return -1;
7511     },
7512     
7513     /**
7514      * Returns the index for a specified column dataIndex.
7515      * @param {String} dataIndex The column dataIndex
7516      * @return {Number} the index, or -1 if not found
7517      */
7518     
7519     findColumnIndex : function(dataIndex){
7520         for(var i = 0, len = this.config.length; i < len; i++){
7521             if(this.config[i].dataIndex == dataIndex){
7522                 return i;
7523             }
7524         }
7525         return -1;
7526     },
7527     
7528     
7529     moveColumn : function(oldIndex, newIndex){
7530         var c = this.config[oldIndex];
7531         this.config.splice(oldIndex, 1);
7532         this.config.splice(newIndex, 0, c);
7533         this.dataMap = null;
7534         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7535     },
7536
7537     isLocked : function(colIndex){
7538         return this.config[colIndex].locked === true;
7539     },
7540
7541     setLocked : function(colIndex, value, suppressEvent){
7542         if(this.isLocked(colIndex) == value){
7543             return;
7544         }
7545         this.config[colIndex].locked = value;
7546         if(!suppressEvent){
7547             this.fireEvent("columnlockchange", this, colIndex, value);
7548         }
7549     },
7550
7551     getTotalLockedWidth : function(){
7552         var totalWidth = 0;
7553         for(var i = 0; i < this.config.length; i++){
7554             if(this.isLocked(i) && !this.isHidden(i)){
7555                 this.totalWidth += this.getColumnWidth(i);
7556             }
7557         }
7558         return totalWidth;
7559     },
7560
7561     getLockedCount : function(){
7562         for(var i = 0, len = this.config.length; i < len; i++){
7563             if(!this.isLocked(i)){
7564                 return i;
7565             }
7566         }
7567         
7568         return this.config.length;
7569     },
7570
7571     /**
7572      * Returns the number of columns.
7573      * @return {Number}
7574      */
7575     getColumnCount : function(visibleOnly){
7576         if(visibleOnly === true){
7577             var c = 0;
7578             for(var i = 0, len = this.config.length; i < len; i++){
7579                 if(!this.isHidden(i)){
7580                     c++;
7581                 }
7582             }
7583             return c;
7584         }
7585         return this.config.length;
7586     },
7587
7588     /**
7589      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7590      * @param {Function} fn
7591      * @param {Object} scope (optional)
7592      * @return {Array} result
7593      */
7594     getColumnsBy : function(fn, scope){
7595         var r = [];
7596         for(var i = 0, len = this.config.length; i < len; i++){
7597             var c = this.config[i];
7598             if(fn.call(scope||this, c, i) === true){
7599                 r[r.length] = c;
7600             }
7601         }
7602         return r;
7603     },
7604
7605     /**
7606      * Returns true if the specified column is sortable.
7607      * @param {Number} col The column index
7608      * @return {Boolean}
7609      */
7610     isSortable : function(col){
7611         if(typeof this.config[col].sortable == "undefined"){
7612             return this.defaultSortable;
7613         }
7614         return this.config[col].sortable;
7615     },
7616
7617     /**
7618      * Returns the rendering (formatting) function defined for the column.
7619      * @param {Number} col The column index.
7620      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7621      */
7622     getRenderer : function(col){
7623         if(!this.config[col].renderer){
7624             return Roo.grid.ColumnModel.defaultRenderer;
7625         }
7626         return this.config[col].renderer;
7627     },
7628
7629     /**
7630      * Sets the rendering (formatting) function for a column.
7631      * @param {Number} col The column index
7632      * @param {Function} fn The function to use to process the cell's raw data
7633      * to return HTML markup for the grid view. The render function is called with
7634      * the following parameters:<ul>
7635      * <li>Data value.</li>
7636      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7637      * <li>css A CSS style string to apply to the table cell.</li>
7638      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7639      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7640      * <li>Row index</li>
7641      * <li>Column index</li>
7642      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7643      */
7644     setRenderer : function(col, fn){
7645         this.config[col].renderer = fn;
7646     },
7647
7648     /**
7649      * Returns the width for the specified column.
7650      * @param {Number} col The column index
7651      * @return {Number}
7652      */
7653     getColumnWidth : function(col){
7654         return this.config[col].width * 1 || this.defaultWidth;
7655     },
7656
7657     /**
7658      * Sets the width for a column.
7659      * @param {Number} col The column index
7660      * @param {Number} width The new width
7661      */
7662     setColumnWidth : function(col, width, suppressEvent){
7663         this.config[col].width = width;
7664         this.totalWidth = null;
7665         if(!suppressEvent){
7666              this.fireEvent("widthchange", this, col, width);
7667         }
7668     },
7669
7670     /**
7671      * Returns the total width of all columns.
7672      * @param {Boolean} includeHidden True to include hidden column widths
7673      * @return {Number}
7674      */
7675     getTotalWidth : function(includeHidden){
7676         if(!this.totalWidth){
7677             this.totalWidth = 0;
7678             for(var i = 0, len = this.config.length; i < len; i++){
7679                 if(includeHidden || !this.isHidden(i)){
7680                     this.totalWidth += this.getColumnWidth(i);
7681                 }
7682             }
7683         }
7684         return this.totalWidth;
7685     },
7686
7687     /**
7688      * Returns the header for the specified column.
7689      * @param {Number} col The column index
7690      * @return {String}
7691      */
7692     getColumnHeader : function(col){
7693         return this.config[col].header;
7694     },
7695
7696     /**
7697      * Sets the header for a column.
7698      * @param {Number} col The column index
7699      * @param {String} header The new header
7700      */
7701     setColumnHeader : function(col, header){
7702         this.config[col].header = header;
7703         this.fireEvent("headerchange", this, col, header);
7704     },
7705
7706     /**
7707      * Returns the tooltip for the specified column.
7708      * @param {Number} col The column index
7709      * @return {String}
7710      */
7711     getColumnTooltip : function(col){
7712             return this.config[col].tooltip;
7713     },
7714     /**
7715      * Sets the tooltip for a column.
7716      * @param {Number} col The column index
7717      * @param {String} tooltip The new tooltip
7718      */
7719     setColumnTooltip : function(col, tooltip){
7720             this.config[col].tooltip = tooltip;
7721     },
7722
7723     /**
7724      * Returns the dataIndex for the specified column.
7725      * @param {Number} col The column index
7726      * @return {Number}
7727      */
7728     getDataIndex : function(col){
7729         return this.config[col].dataIndex;
7730     },
7731
7732     /**
7733      * Sets the dataIndex for a column.
7734      * @param {Number} col The column index
7735      * @param {Number} dataIndex The new dataIndex
7736      */
7737     setDataIndex : function(col, dataIndex){
7738         this.config[col].dataIndex = dataIndex;
7739     },
7740
7741     
7742     
7743     /**
7744      * Returns true if the cell is editable.
7745      * @param {Number} colIndex The column index
7746      * @param {Number} rowIndex The row index - this is nto actually used..?
7747      * @return {Boolean}
7748      */
7749     isCellEditable : function(colIndex, rowIndex){
7750         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7751     },
7752
7753     /**
7754      * Returns the editor defined for the cell/column.
7755      * return false or null to disable editing.
7756      * @param {Number} colIndex The column index
7757      * @param {Number} rowIndex The row index
7758      * @return {Object}
7759      */
7760     getCellEditor : function(colIndex, rowIndex){
7761         return this.config[colIndex].editor;
7762     },
7763
7764     /**
7765      * Sets if a column is editable.
7766      * @param {Number} col The column index
7767      * @param {Boolean} editable True if the column is editable
7768      */
7769     setEditable : function(col, editable){
7770         this.config[col].editable = editable;
7771     },
7772
7773
7774     /**
7775      * Returns true if the column is hidden.
7776      * @param {Number} colIndex The column index
7777      * @return {Boolean}
7778      */
7779     isHidden : function(colIndex){
7780         return this.config[colIndex].hidden;
7781     },
7782
7783
7784     /**
7785      * Returns true if the column width cannot be changed
7786      */
7787     isFixed : function(colIndex){
7788         return this.config[colIndex].fixed;
7789     },
7790
7791     /**
7792      * Returns true if the column can be resized
7793      * @return {Boolean}
7794      */
7795     isResizable : function(colIndex){
7796         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7797     },
7798     /**
7799      * Sets if a column is hidden.
7800      * @param {Number} colIndex The column index
7801      * @param {Boolean} hidden True if the column is hidden
7802      */
7803     setHidden : function(colIndex, hidden){
7804         this.config[colIndex].hidden = hidden;
7805         this.totalWidth = null;
7806         this.fireEvent("hiddenchange", this, colIndex, hidden);
7807     },
7808
7809     /**
7810      * Sets the editor for a column.
7811      * @param {Number} col The column index
7812      * @param {Object} editor The editor object
7813      */
7814     setEditor : function(col, editor){
7815         this.config[col].editor = editor;
7816     },
7817     /**
7818      * Add a column (experimental...) - defaults to adding to the end..
7819      * @param {Object} config 
7820     */
7821     addColumn : function(c)
7822     {
7823     
7824         var i = this.config.length;
7825         this.config[i] = c;
7826         
7827         if(typeof c.dataIndex == "undefined"){
7828             c.dataIndex = i;
7829         }
7830         if(typeof c.renderer == "string"){
7831             c.renderer = Roo.util.Format[c.renderer];
7832         }
7833         if(typeof c.id == "undefined"){
7834             c.id = Roo.id();
7835         }
7836         if(c.editor && c.editor.xtype){
7837             c.editor  = Roo.factory(c.editor, Roo.grid);
7838         }
7839         if(c.editor && c.editor.isFormField){
7840             c.editor = new Roo.grid.GridEditor(c.editor);
7841         }
7842         this.lookup[c.id] = c;
7843     }
7844     
7845 });
7846
7847 Roo.grid.ColumnModel.defaultRenderer = function(value)
7848 {
7849     if(typeof value == "object") {
7850         return value;
7851     }
7852         if(typeof value == "string" && value.length < 1){
7853             return "&#160;";
7854         }
7855     
7856         return String.format("{0}", value);
7857 };
7858
7859 // Alias for backwards compatibility
7860 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7861 /*
7862  * Based on:
7863  * Ext JS Library 1.1.1
7864  * Copyright(c) 2006-2007, Ext JS, LLC.
7865  *
7866  * Originally Released Under LGPL - original licence link has changed is not relivant.
7867  *
7868  * Fork - LGPL
7869  * <script type="text/javascript">
7870  */
7871  
7872 /**
7873  * @class Roo.LoadMask
7874  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7875  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7876  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7877  * element's UpdateManager load indicator and will be destroyed after the initial load.
7878  * @constructor
7879  * Create a new LoadMask
7880  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7881  * @param {Object} config The config object
7882  */
7883 Roo.LoadMask = function(el, config){
7884     this.el = Roo.get(el);
7885     Roo.apply(this, config);
7886     if(this.store){
7887         this.store.on('beforeload', this.onBeforeLoad, this);
7888         this.store.on('load', this.onLoad, this);
7889         this.store.on('loadexception', this.onLoadException, this);
7890         this.removeMask = false;
7891     }else{
7892         var um = this.el.getUpdateManager();
7893         um.showLoadIndicator = false; // disable the default indicator
7894         um.on('beforeupdate', this.onBeforeLoad, this);
7895         um.on('update', this.onLoad, this);
7896         um.on('failure', this.onLoad, this);
7897         this.removeMask = true;
7898     }
7899 };
7900
7901 Roo.LoadMask.prototype = {
7902     /**
7903      * @cfg {Boolean} removeMask
7904      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7905      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7906      */
7907     /**
7908      * @cfg {String} msg
7909      * The text to display in a centered loading message box (defaults to 'Loading...')
7910      */
7911     msg : 'Loading...',
7912     /**
7913      * @cfg {String} msgCls
7914      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7915      */
7916     msgCls : 'x-mask-loading',
7917
7918     /**
7919      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7920      * @type Boolean
7921      */
7922     disabled: false,
7923
7924     /**
7925      * Disables the mask to prevent it from being displayed
7926      */
7927     disable : function(){
7928        this.disabled = true;
7929     },
7930
7931     /**
7932      * Enables the mask so that it can be displayed
7933      */
7934     enable : function(){
7935         this.disabled = false;
7936     },
7937     
7938     onLoadException : function()
7939     {
7940         Roo.log(arguments);
7941         
7942         if (typeof(arguments[3]) != 'undefined') {
7943             Roo.MessageBox.alert("Error loading",arguments[3]);
7944         } 
7945         /*
7946         try {
7947             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7948                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7949             }   
7950         } catch(e) {
7951             
7952         }
7953         */
7954     
7955         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7956     },
7957     // private
7958     onLoad : function()
7959     {
7960         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7961     },
7962
7963     // private
7964     onBeforeLoad : function(){
7965         if(!this.disabled){
7966             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7967         }
7968     },
7969
7970     // private
7971     destroy : function(){
7972         if(this.store){
7973             this.store.un('beforeload', this.onBeforeLoad, this);
7974             this.store.un('load', this.onLoad, this);
7975             this.store.un('loadexception', this.onLoadException, this);
7976         }else{
7977             var um = this.el.getUpdateManager();
7978             um.un('beforeupdate', this.onBeforeLoad, this);
7979             um.un('update', this.onLoad, this);
7980             um.un('failure', this.onLoad, this);
7981         }
7982     }
7983 };/*
7984  * - LGPL
7985  *
7986  * table
7987  * 
7988  */
7989
7990 /**
7991  * @class Roo.bootstrap.Table
7992  * @extends Roo.bootstrap.Component
7993  * Bootstrap Table class
7994  * @cfg {String} cls table class
7995  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7996  * @cfg {String} bgcolor Specifies the background color for a table
7997  * @cfg {Number} border Specifies whether the table cells should have borders or not
7998  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7999  * @cfg {Number} cellspacing Specifies the space between cells
8000  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
8001  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
8002  * @cfg {String} sortable Specifies that the table should be sortable
8003  * @cfg {String} summary Specifies a summary of the content of a table
8004  * @cfg {Number} width Specifies the width of a table
8005  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
8006  * 
8007  * @cfg {boolean} striped Should the rows be alternative striped
8008  * @cfg {boolean} bordered Add borders to the table
8009  * @cfg {boolean} hover Add hover highlighting
8010  * @cfg {boolean} condensed Format condensed
8011  * @cfg {boolean} responsive Format condensed
8012  * @cfg {Boolean} loadMask (true|false) default false
8013  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8014  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8015  * @cfg {Boolean} rowSelection (true|false) default false
8016  * @cfg {Boolean} cellSelection (true|false) default false
8017  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8018  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8019  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8020  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8021  
8022  * 
8023  * @constructor
8024  * Create a new Table
8025  * @param {Object} config The config object
8026  */
8027
8028 Roo.bootstrap.Table = function(config){
8029     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8030     
8031   
8032     
8033     // BC...
8034     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8035     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8036     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8037     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8038     
8039     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8040     if (this.sm) {
8041         this.sm.grid = this;
8042         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8043         this.sm = this.selModel;
8044         this.sm.xmodule = this.xmodule || false;
8045     }
8046     
8047     if (this.cm && typeof(this.cm.config) == 'undefined') {
8048         this.colModel = new Roo.grid.ColumnModel(this.cm);
8049         this.cm = this.colModel;
8050         this.cm.xmodule = this.xmodule || false;
8051     }
8052     if (this.store) {
8053         this.store= Roo.factory(this.store, Roo.data);
8054         this.ds = this.store;
8055         this.ds.xmodule = this.xmodule || false;
8056          
8057     }
8058     if (this.footer && this.store) {
8059         this.footer.dataSource = this.ds;
8060         this.footer = Roo.factory(this.footer);
8061     }
8062     
8063     /** @private */
8064     this.addEvents({
8065         /**
8066          * @event cellclick
8067          * Fires when a cell is clicked
8068          * @param {Roo.bootstrap.Table} this
8069          * @param {Roo.Element} el
8070          * @param {Number} rowIndex
8071          * @param {Number} columnIndex
8072          * @param {Roo.EventObject} e
8073          */
8074         "cellclick" : true,
8075         /**
8076          * @event celldblclick
8077          * Fires when a cell is double clicked
8078          * @param {Roo.bootstrap.Table} this
8079          * @param {Roo.Element} el
8080          * @param {Number} rowIndex
8081          * @param {Number} columnIndex
8082          * @param {Roo.EventObject} e
8083          */
8084         "celldblclick" : true,
8085         /**
8086          * @event rowclick
8087          * Fires when a row is clicked
8088          * @param {Roo.bootstrap.Table} this
8089          * @param {Roo.Element} el
8090          * @param {Number} rowIndex
8091          * @param {Roo.EventObject} e
8092          */
8093         "rowclick" : true,
8094         /**
8095          * @event rowdblclick
8096          * Fires when a row is double clicked
8097          * @param {Roo.bootstrap.Table} this
8098          * @param {Roo.Element} el
8099          * @param {Number} rowIndex
8100          * @param {Roo.EventObject} e
8101          */
8102         "rowdblclick" : true,
8103         /**
8104          * @event mouseover
8105          * Fires when a mouseover occur
8106          * @param {Roo.bootstrap.Table} this
8107          * @param {Roo.Element} el
8108          * @param {Number} rowIndex
8109          * @param {Number} columnIndex
8110          * @param {Roo.EventObject} e
8111          */
8112         "mouseover" : true,
8113         /**
8114          * @event mouseout
8115          * Fires when a mouseout occur
8116          * @param {Roo.bootstrap.Table} this
8117          * @param {Roo.Element} el
8118          * @param {Number} rowIndex
8119          * @param {Number} columnIndex
8120          * @param {Roo.EventObject} e
8121          */
8122         "mouseout" : true,
8123         /**
8124          * @event rowclass
8125          * Fires when a row is rendered, so you can change add a style to it.
8126          * @param {Roo.bootstrap.Table} this
8127          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8128          */
8129         'rowclass' : true,
8130           /**
8131          * @event rowsrendered
8132          * Fires when all the  rows have been rendered
8133          * @param {Roo.bootstrap.Table} this
8134          */
8135         'rowsrendered' : true,
8136         /**
8137          * @event contextmenu
8138          * The raw contextmenu event for the entire grid.
8139          * @param {Roo.EventObject} e
8140          */
8141         "contextmenu" : true,
8142         /**
8143          * @event rowcontextmenu
8144          * Fires when a row is right clicked
8145          * @param {Roo.bootstrap.Table} this
8146          * @param {Number} rowIndex
8147          * @param {Roo.EventObject} e
8148          */
8149         "rowcontextmenu" : true,
8150         /**
8151          * @event cellcontextmenu
8152          * Fires when a cell is right clicked
8153          * @param {Roo.bootstrap.Table} this
8154          * @param {Number} rowIndex
8155          * @param {Number} cellIndex
8156          * @param {Roo.EventObject} e
8157          */
8158          "cellcontextmenu" : true,
8159          /**
8160          * @event headercontextmenu
8161          * Fires when a header is right clicked
8162          * @param {Roo.bootstrap.Table} this
8163          * @param {Number} columnIndex
8164          * @param {Roo.EventObject} e
8165          */
8166         "headercontextmenu" : true
8167     });
8168 };
8169
8170 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8171     
8172     cls: false,
8173     align: false,
8174     bgcolor: false,
8175     border: false,
8176     cellpadding: false,
8177     cellspacing: false,
8178     frame: false,
8179     rules: false,
8180     sortable: false,
8181     summary: false,
8182     width: false,
8183     striped : false,
8184     scrollBody : false,
8185     bordered: false,
8186     hover:  false,
8187     condensed : false,
8188     responsive : false,
8189     sm : false,
8190     cm : false,
8191     store : false,
8192     loadMask : false,
8193     footerShow : true,
8194     headerShow : true,
8195   
8196     rowSelection : false,
8197     cellSelection : false,
8198     layout : false,
8199     
8200     // Roo.Element - the tbody
8201     mainBody: false,
8202     // Roo.Element - thead element
8203     mainHead: false,
8204     
8205     container: false, // used by gridpanel...
8206     
8207     lazyLoad : false,
8208     
8209     CSS : Roo.util.CSS,
8210     
8211     auto_hide_footer : false,
8212     
8213     getAutoCreate : function()
8214     {
8215         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8216         
8217         cfg = {
8218             tag: 'table',
8219             cls : 'table',
8220             cn : []
8221         };
8222         if (this.scrollBody) {
8223             cfg.cls += ' table-body-fixed';
8224         }    
8225         if (this.striped) {
8226             cfg.cls += ' table-striped';
8227         }
8228         
8229         if (this.hover) {
8230             cfg.cls += ' table-hover';
8231         }
8232         if (this.bordered) {
8233             cfg.cls += ' table-bordered';
8234         }
8235         if (this.condensed) {
8236             cfg.cls += ' table-condensed';
8237         }
8238         if (this.responsive) {
8239             cfg.cls += ' table-responsive';
8240         }
8241         
8242         if (this.cls) {
8243             cfg.cls+=  ' ' +this.cls;
8244         }
8245         
8246         // this lot should be simplifed...
8247         var _t = this;
8248         var cp = [
8249             'align',
8250             'bgcolor',
8251             'border',
8252             'cellpadding',
8253             'cellspacing',
8254             'frame',
8255             'rules',
8256             'sortable',
8257             'summary',
8258             'width'
8259         ].forEach(function(k) {
8260             if (_t[k]) {
8261                 cfg[k] = _t[k];
8262             }
8263         });
8264         
8265         
8266         if (this.layout) {
8267             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8268         }
8269         
8270         if(this.store || this.cm){
8271             if(this.headerShow){
8272                 cfg.cn.push(this.renderHeader());
8273             }
8274             
8275             cfg.cn.push(this.renderBody());
8276             
8277             if(this.footerShow){
8278                 cfg.cn.push(this.renderFooter());
8279             }
8280             // where does this come from?
8281             //cfg.cls+=  ' TableGrid';
8282         }
8283         
8284         return { cn : [ cfg ] };
8285     },
8286     
8287     initEvents : function()
8288     {   
8289         if(!this.store || !this.cm){
8290             return;
8291         }
8292         if (this.selModel) {
8293             this.selModel.initEvents();
8294         }
8295         
8296         
8297         //Roo.log('initEvents with ds!!!!');
8298         
8299         this.mainBody = this.el.select('tbody', true).first();
8300         this.mainHead = this.el.select('thead', true).first();
8301         this.mainFoot = this.el.select('tfoot', true).first();
8302         
8303         
8304         
8305         
8306         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8307             e.on('click', this.sort, this);
8308         }, this);
8309         
8310         this.mainBody.on("click", this.onClick, this);
8311         this.mainBody.on("dblclick", this.onDblClick, this);
8312         
8313         // why is this done????? = it breaks dialogs??
8314         //this.parent().el.setStyle('position', 'relative');
8315         
8316         
8317         if (this.footer) {
8318             this.footer.parentId = this.id;
8319             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8320             
8321             if(this.lazyLoad){
8322                 this.el.select('tfoot tr td').first().addClass('hide');
8323             }
8324         } 
8325         
8326         if(this.loadMask) {
8327             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8328         }
8329         
8330         this.store.on('load', this.onLoad, this);
8331         this.store.on('beforeload', this.onBeforeLoad, this);
8332         this.store.on('update', this.onUpdate, this);
8333         this.store.on('add', this.onAdd, this);
8334         this.store.on("clear", this.clear, this);
8335         
8336         this.el.on("contextmenu", this.onContextMenu, this);
8337         
8338         this.mainBody.on('scroll', this.onBodyScroll, this);
8339         
8340         this.cm.on("headerchange", this.onHeaderChange, this);
8341         
8342         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8343         
8344     },
8345     
8346     onContextMenu : function(e, t)
8347     {
8348         this.processEvent("contextmenu", e);
8349     },
8350     
8351     processEvent : function(name, e)
8352     {
8353         if (name != 'touchstart' ) {
8354             this.fireEvent(name, e);    
8355         }
8356         
8357         var t = e.getTarget();
8358         
8359         var cell = Roo.get(t);
8360         
8361         if(!cell){
8362             return;
8363         }
8364         
8365         if(cell.findParent('tfoot', false, true)){
8366             return;
8367         }
8368         
8369         if(cell.findParent('thead', false, true)){
8370             
8371             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8372                 cell = Roo.get(t).findParent('th', false, true);
8373                 if (!cell) {
8374                     Roo.log("failed to find th in thead?");
8375                     Roo.log(e.getTarget());
8376                     return;
8377                 }
8378             }
8379             
8380             var cellIndex = cell.dom.cellIndex;
8381             
8382             var ename = name == 'touchstart' ? 'click' : name;
8383             this.fireEvent("header" + ename, this, cellIndex, e);
8384             
8385             return;
8386         }
8387         
8388         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8389             cell = Roo.get(t).findParent('td', false, true);
8390             if (!cell) {
8391                 Roo.log("failed to find th in tbody?");
8392                 Roo.log(e.getTarget());
8393                 return;
8394             }
8395         }
8396         
8397         var row = cell.findParent('tr', false, true);
8398         var cellIndex = cell.dom.cellIndex;
8399         var rowIndex = row.dom.rowIndex - 1;
8400         
8401         if(row !== false){
8402             
8403             this.fireEvent("row" + name, this, rowIndex, e);
8404             
8405             if(cell !== false){
8406             
8407                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8408             }
8409         }
8410         
8411     },
8412     
8413     onMouseover : function(e, el)
8414     {
8415         var cell = Roo.get(el);
8416         
8417         if(!cell){
8418             return;
8419         }
8420         
8421         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8422             cell = cell.findParent('td', false, true);
8423         }
8424         
8425         var row = cell.findParent('tr', false, true);
8426         var cellIndex = cell.dom.cellIndex;
8427         var rowIndex = row.dom.rowIndex - 1; // start from 0
8428         
8429         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8430         
8431     },
8432     
8433     onMouseout : function(e, el)
8434     {
8435         var cell = Roo.get(el);
8436         
8437         if(!cell){
8438             return;
8439         }
8440         
8441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8442             cell = cell.findParent('td', false, true);
8443         }
8444         
8445         var row = cell.findParent('tr', false, true);
8446         var cellIndex = cell.dom.cellIndex;
8447         var rowIndex = row.dom.rowIndex - 1; // start from 0
8448         
8449         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8450         
8451     },
8452     
8453     onClick : function(e, el)
8454     {
8455         var cell = Roo.get(el);
8456         
8457         if(!cell || (!this.cellSelection && !this.rowSelection)){
8458             return;
8459         }
8460         
8461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8462             cell = cell.findParent('td', false, true);
8463         }
8464         
8465         if(!cell || typeof(cell) == 'undefined'){
8466             return;
8467         }
8468         
8469         var row = cell.findParent('tr', false, true);
8470         
8471         if(!row || typeof(row) == 'undefined'){
8472             return;
8473         }
8474         
8475         var cellIndex = cell.dom.cellIndex;
8476         var rowIndex = this.getRowIndex(row);
8477         
8478         // why??? - should these not be based on SelectionModel?
8479         //if(this.cellSelection){
8480             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8481         //}
8482         
8483         //if(this.rowSelection){
8484             this.fireEvent('rowclick', this, row, rowIndex, e);
8485         //}
8486          
8487     },
8488         
8489     onDblClick : function(e,el)
8490     {
8491         var cell = Roo.get(el);
8492         
8493         if(!cell || (!this.cellSelection && !this.rowSelection)){
8494             return;
8495         }
8496         
8497         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8498             cell = cell.findParent('td', false, true);
8499         }
8500         
8501         if(!cell || typeof(cell) == 'undefined'){
8502             return;
8503         }
8504         
8505         var row = cell.findParent('tr', false, true);
8506         
8507         if(!row || typeof(row) == 'undefined'){
8508             return;
8509         }
8510         
8511         var cellIndex = cell.dom.cellIndex;
8512         var rowIndex = this.getRowIndex(row);
8513         
8514         if(this.cellSelection){
8515             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8516         }
8517         
8518         if(this.rowSelection){
8519             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8520         }
8521     },
8522     
8523     sort : function(e,el)
8524     {
8525         var col = Roo.get(el);
8526         
8527         if(!col.hasClass('sortable')){
8528             return;
8529         }
8530         
8531         var sort = col.attr('sort');
8532         var dir = 'ASC';
8533         
8534         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8535             dir = 'DESC';
8536         }
8537         
8538         this.store.sortInfo = {field : sort, direction : dir};
8539         
8540         if (this.footer) {
8541             Roo.log("calling footer first");
8542             this.footer.onClick('first');
8543         } else {
8544         
8545             this.store.load({ params : { start : 0 } });
8546         }
8547     },
8548     
8549     renderHeader : function()
8550     {
8551         var header = {
8552             tag: 'thead',
8553             cn : []
8554         };
8555         
8556         var cm = this.cm;
8557         this.totalWidth = 0;
8558         
8559         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8560             
8561             var config = cm.config[i];
8562             
8563             var c = {
8564                 tag: 'th',
8565                 cls : 'x-hcol-' + i,
8566                 style : '',
8567                 
8568                 html: cm.getColumnHeader(i)
8569             };
8570             
8571             var tooltip = cm.getColumnTooltip(i);
8572             if (tooltip) {
8573                 c.tooltip = tooltip;
8574             }
8575             
8576             
8577             var hh = '';
8578             
8579             if(typeof(config.sortable) != 'undefined' && config.sortable){
8580                 c.cls = 'sortable';
8581                 c.html = '<i class="fa"></i>' + c.html;
8582             }
8583             
8584             // could use BS4 hidden-..-down 
8585             
8586             if(typeof(config.lgHeader) != 'undefined'){
8587                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8588             }
8589             
8590             if(typeof(config.mdHeader) != 'undefined'){
8591                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8592             }
8593             
8594             if(typeof(config.smHeader) != 'undefined'){
8595                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8596             }
8597             
8598             if(typeof(config.xsHeader) != 'undefined'){
8599                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8600             }
8601             
8602             if(hh.length){
8603                 c.html = hh;
8604             }
8605             
8606             if(typeof(config.tooltip) != 'undefined'){
8607                 c.tooltip = config.tooltip;
8608             }
8609             
8610             if(typeof(config.colspan) != 'undefined'){
8611                 c.colspan = config.colspan;
8612             }
8613             
8614             if(typeof(config.hidden) != 'undefined' && config.hidden){
8615                 c.style += ' display:none;';
8616             }
8617             
8618             if(typeof(config.dataIndex) != 'undefined'){
8619                 c.sort = config.dataIndex;
8620             }
8621             
8622            
8623             
8624             if(typeof(config.align) != 'undefined' && config.align.length){
8625                 c.style += ' text-align:' + config.align + ';';
8626             }
8627             
8628             if(typeof(config.width) != 'undefined'){
8629                 c.style += ' width:' + config.width + 'px;';
8630                 this.totalWidth += config.width;
8631             } else {
8632                 this.totalWidth += 100; // assume minimum of 100 per column?
8633             }
8634             
8635             if(typeof(config.cls) != 'undefined'){
8636                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8637             }
8638             
8639             ['xs','sm','md','lg'].map(function(size){
8640                 
8641                 if(typeof(config[size]) == 'undefined'){
8642                     return;
8643                 }
8644                  
8645                 if (!config[size]) { // 0 = hidden
8646                     // BS 4 '0' is treated as hide that column and below.
8647                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8648                     return;
8649                 }
8650                 
8651                 c.cls += ' col-' + size + '-' + config[size] + (
8652                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8653                 );
8654                 
8655                 
8656             });
8657             
8658             header.cn.push(c)
8659         }
8660         
8661         return header;
8662     },
8663     
8664     renderBody : function()
8665     {
8666         var body = {
8667             tag: 'tbody',
8668             cn : [
8669                 {
8670                     tag: 'tr',
8671                     cn : [
8672                         {
8673                             tag : 'td',
8674                             colspan :  this.cm.getColumnCount()
8675                         }
8676                     ]
8677                 }
8678             ]
8679         };
8680         
8681         return body;
8682     },
8683     
8684     renderFooter : function()
8685     {
8686         var footer = {
8687             tag: 'tfoot',
8688             cn : [
8689                 {
8690                     tag: 'tr',
8691                     cn : [
8692                         {
8693                             tag : 'td',
8694                             colspan :  this.cm.getColumnCount()
8695                         }
8696                     ]
8697                 }
8698             ]
8699         };
8700         
8701         return footer;
8702     },
8703     
8704     
8705     
8706     onLoad : function()
8707     {
8708 //        Roo.log('ds onload');
8709         this.clear();
8710         
8711         var _this = this;
8712         var cm = this.cm;
8713         var ds = this.store;
8714         
8715         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8716             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8717             if (_this.store.sortInfo) {
8718                     
8719                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8720                     e.select('i', true).addClass(['fa-arrow-up']);
8721                 }
8722                 
8723                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8724                     e.select('i', true).addClass(['fa-arrow-down']);
8725                 }
8726             }
8727         });
8728         
8729         var tbody =  this.mainBody;
8730               
8731         if(ds.getCount() > 0){
8732             ds.data.each(function(d,rowIndex){
8733                 var row =  this.renderRow(cm, ds, rowIndex);
8734                 
8735                 tbody.createChild(row);
8736                 
8737                 var _this = this;
8738                 
8739                 if(row.cellObjects.length){
8740                     Roo.each(row.cellObjects, function(r){
8741                         _this.renderCellObject(r);
8742                     })
8743                 }
8744                 
8745             }, this);
8746         }
8747         
8748         var tfoot = this.el.select('tfoot', true).first();
8749         
8750         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8751             
8752             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8753             
8754             var total = this.ds.getTotalCount();
8755             
8756             if(this.footer.pageSize < total){
8757                 this.mainFoot.show();
8758             }
8759         }
8760         
8761         Roo.each(this.el.select('tbody td', true).elements, function(e){
8762             e.on('mouseover', _this.onMouseover, _this);
8763         });
8764         
8765         Roo.each(this.el.select('tbody td', true).elements, function(e){
8766             e.on('mouseout', _this.onMouseout, _this);
8767         });
8768         this.fireEvent('rowsrendered', this);
8769         
8770         this.autoSize();
8771     },
8772     
8773     
8774     onUpdate : function(ds,record)
8775     {
8776         this.refreshRow(record);
8777         this.autoSize();
8778     },
8779     
8780     onRemove : function(ds, record, index, isUpdate){
8781         if(isUpdate !== true){
8782             this.fireEvent("beforerowremoved", this, index, record);
8783         }
8784         var bt = this.mainBody.dom;
8785         
8786         var rows = this.el.select('tbody > tr', true).elements;
8787         
8788         if(typeof(rows[index]) != 'undefined'){
8789             bt.removeChild(rows[index].dom);
8790         }
8791         
8792 //        if(bt.rows[index]){
8793 //            bt.removeChild(bt.rows[index]);
8794 //        }
8795         
8796         if(isUpdate !== true){
8797             //this.stripeRows(index);
8798             //this.syncRowHeights(index, index);
8799             //this.layout();
8800             this.fireEvent("rowremoved", this, index, record);
8801         }
8802     },
8803     
8804     onAdd : function(ds, records, rowIndex)
8805     {
8806         //Roo.log('on Add called');
8807         // - note this does not handle multiple adding very well..
8808         var bt = this.mainBody.dom;
8809         for (var i =0 ; i < records.length;i++) {
8810             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8811             //Roo.log(records[i]);
8812             //Roo.log(this.store.getAt(rowIndex+i));
8813             this.insertRow(this.store, rowIndex + i, false);
8814             return;
8815         }
8816         
8817     },
8818     
8819     
8820     refreshRow : function(record){
8821         var ds = this.store, index;
8822         if(typeof record == 'number'){
8823             index = record;
8824             record = ds.getAt(index);
8825         }else{
8826             index = ds.indexOf(record);
8827             if (index < 0) {
8828                 return; // should not happen - but seems to 
8829             }
8830         }
8831         this.insertRow(ds, index, true);
8832         this.autoSize();
8833         this.onRemove(ds, record, index+1, true);
8834         this.autoSize();
8835         //this.syncRowHeights(index, index);
8836         //this.layout();
8837         this.fireEvent("rowupdated", this, index, record);
8838     },
8839     
8840     insertRow : function(dm, rowIndex, isUpdate){
8841         
8842         if(!isUpdate){
8843             this.fireEvent("beforerowsinserted", this, rowIndex);
8844         }
8845             //var s = this.getScrollState();
8846         var row = this.renderRow(this.cm, this.store, rowIndex);
8847         // insert before rowIndex..
8848         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8849         
8850         var _this = this;
8851                 
8852         if(row.cellObjects.length){
8853             Roo.each(row.cellObjects, function(r){
8854                 _this.renderCellObject(r);
8855             })
8856         }
8857             
8858         if(!isUpdate){
8859             this.fireEvent("rowsinserted", this, rowIndex);
8860             //this.syncRowHeights(firstRow, lastRow);
8861             //this.stripeRows(firstRow);
8862             //this.layout();
8863         }
8864         
8865     },
8866     
8867     
8868     getRowDom : function(rowIndex)
8869     {
8870         var rows = this.el.select('tbody > tr', true).elements;
8871         
8872         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8873         
8874     },
8875     // returns the object tree for a tr..
8876   
8877     
8878     renderRow : function(cm, ds, rowIndex) 
8879     {
8880         var d = ds.getAt(rowIndex);
8881         
8882         var row = {
8883             tag : 'tr',
8884             cls : 'x-row-' + rowIndex,
8885             cn : []
8886         };
8887             
8888         var cellObjects = [];
8889         
8890         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8891             var config = cm.config[i];
8892             
8893             var renderer = cm.getRenderer(i);
8894             var value = '';
8895             var id = false;
8896             
8897             if(typeof(renderer) !== 'undefined'){
8898                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8899             }
8900             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8901             // and are rendered into the cells after the row is rendered - using the id for the element.
8902             
8903             if(typeof(value) === 'object'){
8904                 id = Roo.id();
8905                 cellObjects.push({
8906                     container : id,
8907                     cfg : value 
8908                 })
8909             }
8910             
8911             var rowcfg = {
8912                 record: d,
8913                 rowIndex : rowIndex,
8914                 colIndex : i,
8915                 rowClass : ''
8916             };
8917
8918             this.fireEvent('rowclass', this, rowcfg);
8919             
8920             var td = {
8921                 tag: 'td',
8922                 // this might end up displaying HTML?
8923                 // this is too messy... - better to only do it on columsn you know are going to be too long
8924                 //tooltip : (typeof(value) === 'object') ? '' : value,
8925                 cls : rowcfg.rowClass + ' x-col-' + i,
8926                 style: '',
8927                 html: (typeof(value) === 'object') ? '' : value
8928             };
8929             
8930             if (id) {
8931                 td.id = id;
8932             }
8933             
8934             if(typeof(config.colspan) != 'undefined'){
8935                 td.colspan = config.colspan;
8936             }
8937             
8938             if(typeof(config.hidden) != 'undefined' && config.hidden){
8939                 td.style += ' display:none;';
8940             }
8941             
8942             if(typeof(config.align) != 'undefined' && config.align.length){
8943                 td.style += ' text-align:' + config.align + ';';
8944             }
8945             if(typeof(config.valign) != 'undefined' && config.valign.length){
8946                 td.style += ' vertical-align:' + config.valign + ';';
8947             }
8948             
8949             if(typeof(config.width) != 'undefined'){
8950                 td.style += ' width:' +  config.width + 'px;';
8951             }
8952             
8953             if(typeof(config.cursor) != 'undefined'){
8954                 td.style += ' cursor:' +  config.cursor + ';';
8955             }
8956             
8957             if(typeof(config.cls) != 'undefined'){
8958                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8959             }
8960             
8961             ['xs','sm','md','lg'].map(function(size){
8962                 
8963                 if(typeof(config[size]) == 'undefined'){
8964                     return;
8965                 }
8966                 
8967                 
8968                   
8969                 if (!config[size]) { // 0 = hidden
8970                     // BS 4 '0' is treated as hide that column and below.
8971                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8972                     return;
8973                 }
8974                 
8975                 td.cls += ' col-' + size + '-' + config[size] + (
8976                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8977                 );
8978                  
8979
8980             });
8981             
8982             row.cn.push(td);
8983            
8984         }
8985         
8986         row.cellObjects = cellObjects;
8987         
8988         return row;
8989           
8990     },
8991     
8992     
8993     
8994     onBeforeLoad : function()
8995     {
8996         
8997     },
8998      /**
8999      * Remove all rows
9000      */
9001     clear : function()
9002     {
9003         this.el.select('tbody', true).first().dom.innerHTML = '';
9004     },
9005     /**
9006      * Show or hide a row.
9007      * @param {Number} rowIndex to show or hide
9008      * @param {Boolean} state hide
9009      */
9010     setRowVisibility : function(rowIndex, state)
9011     {
9012         var bt = this.mainBody.dom;
9013         
9014         var rows = this.el.select('tbody > tr', true).elements;
9015         
9016         if(typeof(rows[rowIndex]) == 'undefined'){
9017             return;
9018         }
9019         rows[rowIndex].dom.style.display = state ? '' : 'none';
9020     },
9021     
9022     
9023     getSelectionModel : function(){
9024         if(!this.selModel){
9025             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9026         }
9027         return this.selModel;
9028     },
9029     /*
9030      * Render the Roo.bootstrap object from renderder
9031      */
9032     renderCellObject : function(r)
9033     {
9034         var _this = this;
9035         
9036         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9037         
9038         var t = r.cfg.render(r.container);
9039         
9040         if(r.cfg.cn){
9041             Roo.each(r.cfg.cn, function(c){
9042                 var child = {
9043                     container: t.getChildContainer(),
9044                     cfg: c
9045                 };
9046                 _this.renderCellObject(child);
9047             })
9048         }
9049     },
9050     
9051     getRowIndex : function(row)
9052     {
9053         var rowIndex = -1;
9054         
9055         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9056             if(el != row){
9057                 return;
9058             }
9059             
9060             rowIndex = index;
9061         });
9062         
9063         return rowIndex;
9064     },
9065      /**
9066      * Returns the grid's underlying element = used by panel.Grid
9067      * @return {Element} The element
9068      */
9069     getGridEl : function(){
9070         return this.el;
9071     },
9072      /**
9073      * Forces a resize - used by panel.Grid
9074      * @return {Element} The element
9075      */
9076     autoSize : function()
9077     {
9078         //var ctr = Roo.get(this.container.dom.parentElement);
9079         var ctr = Roo.get(this.el.dom);
9080         
9081         var thd = this.getGridEl().select('thead',true).first();
9082         var tbd = this.getGridEl().select('tbody', true).first();
9083         var tfd = this.getGridEl().select('tfoot', true).first();
9084         
9085         var cw = ctr.getWidth();
9086         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9087         
9088         if (tbd) {
9089             
9090             tbd.setWidth(ctr.getWidth());
9091             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9092             // this needs fixing for various usage - currently only hydra job advers I think..
9093             //tdb.setHeight(
9094             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9095             //); 
9096             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9097             cw -= barsize;
9098         }
9099         cw = Math.max(cw, this.totalWidth);
9100         this.getGridEl().select('tbody tr',true).setWidth(cw);
9101         
9102         // resize 'expandable coloumn?
9103         
9104         return; // we doe not have a view in this design..
9105         
9106     },
9107     onBodyScroll: function()
9108     {
9109         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9110         if(this.mainHead){
9111             this.mainHead.setStyle({
9112                 'position' : 'relative',
9113                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9114             });
9115         }
9116         
9117         if(this.lazyLoad){
9118             
9119             var scrollHeight = this.mainBody.dom.scrollHeight;
9120             
9121             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9122             
9123             var height = this.mainBody.getHeight();
9124             
9125             if(scrollHeight - height == scrollTop) {
9126                 
9127                 var total = this.ds.getTotalCount();
9128                 
9129                 if(this.footer.cursor + this.footer.pageSize < total){
9130                     
9131                     this.footer.ds.load({
9132                         params : {
9133                             start : this.footer.cursor + this.footer.pageSize,
9134                             limit : this.footer.pageSize
9135                         },
9136                         add : true
9137                     });
9138                 }
9139             }
9140             
9141         }
9142     },
9143     
9144     onHeaderChange : function()
9145     {
9146         var header = this.renderHeader();
9147         var table = this.el.select('table', true).first();
9148         
9149         this.mainHead.remove();
9150         this.mainHead = table.createChild(header, this.mainBody, false);
9151         
9152         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9153             e.on('click', this.sort, this);
9154         }, this);
9155         
9156         
9157     },
9158     
9159     onHiddenChange : function(colModel, colIndex, hidden)
9160     {
9161         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9162         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9163         
9164         this.CSS.updateRule(thSelector, "display", "");
9165         this.CSS.updateRule(tdSelector, "display", "");
9166         
9167         if(hidden){
9168             this.CSS.updateRule(thSelector, "display", "none");
9169             this.CSS.updateRule(tdSelector, "display", "none");
9170         }
9171         
9172         this.onHeaderChange();
9173         this.onLoad();
9174     },
9175     
9176     setColumnWidth: function(col_index, width)
9177     {
9178         // width = "md-2 xs-2..."
9179         if(!this.colModel.config[col_index]) {
9180             return;
9181         }
9182         
9183         var w = width.split(" ");
9184         
9185         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9186         
9187         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9188         
9189         
9190         for(var j = 0; j < w.length; j++) {
9191             
9192             if(!w[j]) {
9193                 continue;
9194             }
9195             
9196             var size_cls = w[j].split("-");
9197             
9198             if(!Number.isInteger(size_cls[1] * 1)) {
9199                 continue;
9200             }
9201             
9202             if(!this.colModel.config[col_index][size_cls[0]]) {
9203                 continue;
9204             }
9205             
9206             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9207                 continue;
9208             }
9209             
9210             h_row[0].classList.replace(
9211                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9212                 "col-"+size_cls[0]+"-"+size_cls[1]
9213             );
9214             
9215             for(var i = 0; i < rows.length; i++) {
9216                 
9217                 var size_cls = w[j].split("-");
9218                 
9219                 if(!Number.isInteger(size_cls[1] * 1)) {
9220                     continue;
9221                 }
9222                 
9223                 if(!this.colModel.config[col_index][size_cls[0]]) {
9224                     continue;
9225                 }
9226                 
9227                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9228                     continue;
9229                 }
9230                 
9231                 rows[i].classList.replace(
9232                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9233                     "col-"+size_cls[0]+"-"+size_cls[1]
9234                 );
9235             }
9236             
9237             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9238         }
9239     }
9240 });
9241
9242  
9243
9244  /*
9245  * - LGPL
9246  *
9247  * table cell
9248  * 
9249  */
9250
9251 /**
9252  * @class Roo.bootstrap.TableCell
9253  * @extends Roo.bootstrap.Component
9254  * Bootstrap TableCell class
9255  * @cfg {String} html cell contain text
9256  * @cfg {String} cls cell class
9257  * @cfg {String} tag cell tag (td|th) default td
9258  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9259  * @cfg {String} align Aligns the content in a cell
9260  * @cfg {String} axis Categorizes cells
9261  * @cfg {String} bgcolor Specifies the background color of a cell
9262  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9263  * @cfg {Number} colspan Specifies the number of columns a cell should span
9264  * @cfg {String} headers Specifies one or more header cells a cell is related to
9265  * @cfg {Number} height Sets the height of a cell
9266  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9267  * @cfg {Number} rowspan Sets the number of rows a cell should span
9268  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9269  * @cfg {String} valign Vertical aligns the content in a cell
9270  * @cfg {Number} width Specifies the width of a cell
9271  * 
9272  * @constructor
9273  * Create a new TableCell
9274  * @param {Object} config The config object
9275  */
9276
9277 Roo.bootstrap.TableCell = function(config){
9278     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9279 };
9280
9281 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9282     
9283     html: false,
9284     cls: false,
9285     tag: false,
9286     abbr: false,
9287     align: false,
9288     axis: false,
9289     bgcolor: false,
9290     charoff: false,
9291     colspan: false,
9292     headers: false,
9293     height: false,
9294     nowrap: false,
9295     rowspan: false,
9296     scope: false,
9297     valign: false,
9298     width: false,
9299     
9300     
9301     getAutoCreate : function(){
9302         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9303         
9304         cfg = {
9305             tag: 'td'
9306         };
9307         
9308         if(this.tag){
9309             cfg.tag = this.tag;
9310         }
9311         
9312         if (this.html) {
9313             cfg.html=this.html
9314         }
9315         if (this.cls) {
9316             cfg.cls=this.cls
9317         }
9318         if (this.abbr) {
9319             cfg.abbr=this.abbr
9320         }
9321         if (this.align) {
9322             cfg.align=this.align
9323         }
9324         if (this.axis) {
9325             cfg.axis=this.axis
9326         }
9327         if (this.bgcolor) {
9328             cfg.bgcolor=this.bgcolor
9329         }
9330         if (this.charoff) {
9331             cfg.charoff=this.charoff
9332         }
9333         if (this.colspan) {
9334             cfg.colspan=this.colspan
9335         }
9336         if (this.headers) {
9337             cfg.headers=this.headers
9338         }
9339         if (this.height) {
9340             cfg.height=this.height
9341         }
9342         if (this.nowrap) {
9343             cfg.nowrap=this.nowrap
9344         }
9345         if (this.rowspan) {
9346             cfg.rowspan=this.rowspan
9347         }
9348         if (this.scope) {
9349             cfg.scope=this.scope
9350         }
9351         if (this.valign) {
9352             cfg.valign=this.valign
9353         }
9354         if (this.width) {
9355             cfg.width=this.width
9356         }
9357         
9358         
9359         return cfg;
9360     }
9361    
9362 });
9363
9364  
9365
9366  /*
9367  * - LGPL
9368  *
9369  * table row
9370  * 
9371  */
9372
9373 /**
9374  * @class Roo.bootstrap.TableRow
9375  * @extends Roo.bootstrap.Component
9376  * Bootstrap TableRow class
9377  * @cfg {String} cls row class
9378  * @cfg {String} align Aligns the content in a table row
9379  * @cfg {String} bgcolor Specifies a background color for a table row
9380  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9381  * @cfg {String} valign Vertical aligns the content in a table row
9382  * 
9383  * @constructor
9384  * Create a new TableRow
9385  * @param {Object} config The config object
9386  */
9387
9388 Roo.bootstrap.TableRow = function(config){
9389     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9390 };
9391
9392 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9393     
9394     cls: false,
9395     align: false,
9396     bgcolor: false,
9397     charoff: false,
9398     valign: false,
9399     
9400     getAutoCreate : function(){
9401         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9402         
9403         cfg = {
9404             tag: 'tr'
9405         };
9406             
9407         if(this.cls){
9408             cfg.cls = this.cls;
9409         }
9410         if(this.align){
9411             cfg.align = this.align;
9412         }
9413         if(this.bgcolor){
9414             cfg.bgcolor = this.bgcolor;
9415         }
9416         if(this.charoff){
9417             cfg.charoff = this.charoff;
9418         }
9419         if(this.valign){
9420             cfg.valign = this.valign;
9421         }
9422         
9423         return cfg;
9424     }
9425    
9426 });
9427
9428  
9429
9430  /*
9431  * - LGPL
9432  *
9433  * table body
9434  * 
9435  */
9436
9437 /**
9438  * @class Roo.bootstrap.TableBody
9439  * @extends Roo.bootstrap.Component
9440  * Bootstrap TableBody class
9441  * @cfg {String} cls element class
9442  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9443  * @cfg {String} align Aligns the content inside the element
9444  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9445  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9446  * 
9447  * @constructor
9448  * Create a new TableBody
9449  * @param {Object} config The config object
9450  */
9451
9452 Roo.bootstrap.TableBody = function(config){
9453     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9454 };
9455
9456 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9457     
9458     cls: false,
9459     tag: false,
9460     align: false,
9461     charoff: false,
9462     valign: false,
9463     
9464     getAutoCreate : function(){
9465         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9466         
9467         cfg = {
9468             tag: 'tbody'
9469         };
9470             
9471         if (this.cls) {
9472             cfg.cls=this.cls
9473         }
9474         if(this.tag){
9475             cfg.tag = this.tag;
9476         }
9477         
9478         if(this.align){
9479             cfg.align = this.align;
9480         }
9481         if(this.charoff){
9482             cfg.charoff = this.charoff;
9483         }
9484         if(this.valign){
9485             cfg.valign = this.valign;
9486         }
9487         
9488         return cfg;
9489     }
9490     
9491     
9492 //    initEvents : function()
9493 //    {
9494 //        
9495 //        if(!this.store){
9496 //            return;
9497 //        }
9498 //        
9499 //        this.store = Roo.factory(this.store, Roo.data);
9500 //        this.store.on('load', this.onLoad, this);
9501 //        
9502 //        this.store.load();
9503 //        
9504 //    },
9505 //    
9506 //    onLoad: function () 
9507 //    {   
9508 //        this.fireEvent('load', this);
9509 //    }
9510 //    
9511 //   
9512 });
9513
9514  
9515
9516  /*
9517  * Based on:
9518  * Ext JS Library 1.1.1
9519  * Copyright(c) 2006-2007, Ext JS, LLC.
9520  *
9521  * Originally Released Under LGPL - original licence link has changed is not relivant.
9522  *
9523  * Fork - LGPL
9524  * <script type="text/javascript">
9525  */
9526
9527 // as we use this in bootstrap.
9528 Roo.namespace('Roo.form');
9529  /**
9530  * @class Roo.form.Action
9531  * Internal Class used to handle form actions
9532  * @constructor
9533  * @param {Roo.form.BasicForm} el The form element or its id
9534  * @param {Object} config Configuration options
9535  */
9536
9537  
9538  
9539 // define the action interface
9540 Roo.form.Action = function(form, options){
9541     this.form = form;
9542     this.options = options || {};
9543 };
9544 /**
9545  * Client Validation Failed
9546  * @const 
9547  */
9548 Roo.form.Action.CLIENT_INVALID = 'client';
9549 /**
9550  * Server Validation Failed
9551  * @const 
9552  */
9553 Roo.form.Action.SERVER_INVALID = 'server';
9554  /**
9555  * Connect to Server Failed
9556  * @const 
9557  */
9558 Roo.form.Action.CONNECT_FAILURE = 'connect';
9559 /**
9560  * Reading Data from Server Failed
9561  * @const 
9562  */
9563 Roo.form.Action.LOAD_FAILURE = 'load';
9564
9565 Roo.form.Action.prototype = {
9566     type : 'default',
9567     failureType : undefined,
9568     response : undefined,
9569     result : undefined,
9570
9571     // interface method
9572     run : function(options){
9573
9574     },
9575
9576     // interface method
9577     success : function(response){
9578
9579     },
9580
9581     // interface method
9582     handleResponse : function(response){
9583
9584     },
9585
9586     // default connection failure
9587     failure : function(response){
9588         
9589         this.response = response;
9590         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9591         this.form.afterAction(this, false);
9592     },
9593
9594     processResponse : function(response){
9595         this.response = response;
9596         if(!response.responseText){
9597             return true;
9598         }
9599         this.result = this.handleResponse(response);
9600         return this.result;
9601     },
9602
9603     // utility functions used internally
9604     getUrl : function(appendParams){
9605         var url = this.options.url || this.form.url || this.form.el.dom.action;
9606         if(appendParams){
9607             var p = this.getParams();
9608             if(p){
9609                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9610             }
9611         }
9612         return url;
9613     },
9614
9615     getMethod : function(){
9616         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9617     },
9618
9619     getParams : function(){
9620         var bp = this.form.baseParams;
9621         var p = this.options.params;
9622         if(p){
9623             if(typeof p == "object"){
9624                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9625             }else if(typeof p == 'string' && bp){
9626                 p += '&' + Roo.urlEncode(bp);
9627             }
9628         }else if(bp){
9629             p = Roo.urlEncode(bp);
9630         }
9631         return p;
9632     },
9633
9634     createCallback : function(){
9635         return {
9636             success: this.success,
9637             failure: this.failure,
9638             scope: this,
9639             timeout: (this.form.timeout*1000),
9640             upload: this.form.fileUpload ? this.success : undefined
9641         };
9642     }
9643 };
9644
9645 Roo.form.Action.Submit = function(form, options){
9646     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9647 };
9648
9649 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9650     type : 'submit',
9651
9652     haveProgress : false,
9653     uploadComplete : false,
9654     
9655     // uploadProgress indicator.
9656     uploadProgress : function()
9657     {
9658         if (!this.form.progressUrl) {
9659             return;
9660         }
9661         
9662         if (!this.haveProgress) {
9663             Roo.MessageBox.progress("Uploading", "Uploading");
9664         }
9665         if (this.uploadComplete) {
9666            Roo.MessageBox.hide();
9667            return;
9668         }
9669         
9670         this.haveProgress = true;
9671    
9672         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9673         
9674         var c = new Roo.data.Connection();
9675         c.request({
9676             url : this.form.progressUrl,
9677             params: {
9678                 id : uid
9679             },
9680             method: 'GET',
9681             success : function(req){
9682                //console.log(data);
9683                 var rdata = false;
9684                 var edata;
9685                 try  {
9686                    rdata = Roo.decode(req.responseText)
9687                 } catch (e) {
9688                     Roo.log("Invalid data from server..");
9689                     Roo.log(edata);
9690                     return;
9691                 }
9692                 if (!rdata || !rdata.success) {
9693                     Roo.log(rdata);
9694                     Roo.MessageBox.alert(Roo.encode(rdata));
9695                     return;
9696                 }
9697                 var data = rdata.data;
9698                 
9699                 if (this.uploadComplete) {
9700                    Roo.MessageBox.hide();
9701                    return;
9702                 }
9703                    
9704                 if (data){
9705                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9706                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9707                     );
9708                 }
9709                 this.uploadProgress.defer(2000,this);
9710             },
9711        
9712             failure: function(data) {
9713                 Roo.log('progress url failed ');
9714                 Roo.log(data);
9715             },
9716             scope : this
9717         });
9718            
9719     },
9720     
9721     
9722     run : function()
9723     {
9724         // run get Values on the form, so it syncs any secondary forms.
9725         this.form.getValues();
9726         
9727         var o = this.options;
9728         var method = this.getMethod();
9729         var isPost = method == 'POST';
9730         if(o.clientValidation === false || this.form.isValid()){
9731             
9732             if (this.form.progressUrl) {
9733                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9734                     (new Date() * 1) + '' + Math.random());
9735                     
9736             } 
9737             
9738             
9739             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9740                 form:this.form.el.dom,
9741                 url:this.getUrl(!isPost),
9742                 method: method,
9743                 params:isPost ? this.getParams() : null,
9744                 isUpload: this.form.fileUpload,
9745                 formData : this.form.formData
9746             }));
9747             
9748             this.uploadProgress();
9749
9750         }else if (o.clientValidation !== false){ // client validation failed
9751             this.failureType = Roo.form.Action.CLIENT_INVALID;
9752             this.form.afterAction(this, false);
9753         }
9754     },
9755
9756     success : function(response)
9757     {
9758         this.uploadComplete= true;
9759         if (this.haveProgress) {
9760             Roo.MessageBox.hide();
9761         }
9762         
9763         
9764         var result = this.processResponse(response);
9765         if(result === true || result.success){
9766             this.form.afterAction(this, true);
9767             return;
9768         }
9769         if(result.errors){
9770             this.form.markInvalid(result.errors);
9771             this.failureType = Roo.form.Action.SERVER_INVALID;
9772         }
9773         this.form.afterAction(this, false);
9774     },
9775     failure : function(response)
9776     {
9777         this.uploadComplete= true;
9778         if (this.haveProgress) {
9779             Roo.MessageBox.hide();
9780         }
9781         
9782         this.response = response;
9783         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9784         this.form.afterAction(this, false);
9785     },
9786     
9787     handleResponse : function(response){
9788         if(this.form.errorReader){
9789             var rs = this.form.errorReader.read(response);
9790             var errors = [];
9791             if(rs.records){
9792                 for(var i = 0, len = rs.records.length; i < len; i++) {
9793                     var r = rs.records[i];
9794                     errors[i] = r.data;
9795                 }
9796             }
9797             if(errors.length < 1){
9798                 errors = null;
9799             }
9800             return {
9801                 success : rs.success,
9802                 errors : errors
9803             };
9804         }
9805         var ret = false;
9806         try {
9807             ret = Roo.decode(response.responseText);
9808         } catch (e) {
9809             ret = {
9810                 success: false,
9811                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9812                 errors : []
9813             };
9814         }
9815         return ret;
9816         
9817     }
9818 });
9819
9820
9821 Roo.form.Action.Load = function(form, options){
9822     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9823     this.reader = this.form.reader;
9824 };
9825
9826 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9827     type : 'load',
9828
9829     run : function(){
9830         
9831         Roo.Ajax.request(Roo.apply(
9832                 this.createCallback(), {
9833                     method:this.getMethod(),
9834                     url:this.getUrl(false),
9835                     params:this.getParams()
9836         }));
9837     },
9838
9839     success : function(response){
9840         
9841         var result = this.processResponse(response);
9842         if(result === true || !result.success || !result.data){
9843             this.failureType = Roo.form.Action.LOAD_FAILURE;
9844             this.form.afterAction(this, false);
9845             return;
9846         }
9847         this.form.clearInvalid();
9848         this.form.setValues(result.data);
9849         this.form.afterAction(this, true);
9850     },
9851
9852     handleResponse : function(response){
9853         if(this.form.reader){
9854             var rs = this.form.reader.read(response);
9855             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9856             return {
9857                 success : rs.success,
9858                 data : data
9859             };
9860         }
9861         return Roo.decode(response.responseText);
9862     }
9863 });
9864
9865 Roo.form.Action.ACTION_TYPES = {
9866     'load' : Roo.form.Action.Load,
9867     'submit' : Roo.form.Action.Submit
9868 };/*
9869  * - LGPL
9870  *
9871  * form
9872  *
9873  */
9874
9875 /**
9876  * @class Roo.bootstrap.Form
9877  * @extends Roo.bootstrap.Component
9878  * Bootstrap Form class
9879  * @cfg {String} method  GET | POST (default POST)
9880  * @cfg {String} labelAlign top | left (default top)
9881  * @cfg {String} align left  | right - for navbars
9882  * @cfg {Boolean} loadMask load mask when submit (default true)
9883
9884  *
9885  * @constructor
9886  * Create a new Form
9887  * @param {Object} config The config object
9888  */
9889
9890
9891 Roo.bootstrap.Form = function(config){
9892     
9893     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9894     
9895     Roo.bootstrap.Form.popover.apply();
9896     
9897     this.addEvents({
9898         /**
9899          * @event clientvalidation
9900          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9901          * @param {Form} this
9902          * @param {Boolean} valid true if the form has passed client-side validation
9903          */
9904         clientvalidation: true,
9905         /**
9906          * @event beforeaction
9907          * Fires before any action is performed. Return false to cancel the action.
9908          * @param {Form} this
9909          * @param {Action} action The action to be performed
9910          */
9911         beforeaction: true,
9912         /**
9913          * @event actionfailed
9914          * Fires when an action fails.
9915          * @param {Form} this
9916          * @param {Action} action The action that failed
9917          */
9918         actionfailed : true,
9919         /**
9920          * @event actioncomplete
9921          * Fires when an action is completed.
9922          * @param {Form} this
9923          * @param {Action} action The action that completed
9924          */
9925         actioncomplete : true
9926     });
9927 };
9928
9929 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9930
9931      /**
9932      * @cfg {String} method
9933      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9934      */
9935     method : 'POST',
9936     /**
9937      * @cfg {String} url
9938      * The URL to use for form actions if one isn't supplied in the action options.
9939      */
9940     /**
9941      * @cfg {Boolean} fileUpload
9942      * Set to true if this form is a file upload.
9943      */
9944
9945     /**
9946      * @cfg {Object} baseParams
9947      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9948      */
9949
9950     /**
9951      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9952      */
9953     timeout: 30,
9954     /**
9955      * @cfg {Sting} align (left|right) for navbar forms
9956      */
9957     align : 'left',
9958
9959     // private
9960     activeAction : null,
9961
9962     /**
9963      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9964      * element by passing it or its id or mask the form itself by passing in true.
9965      * @type Mixed
9966      */
9967     waitMsgTarget : false,
9968
9969     loadMask : true,
9970     
9971     /**
9972      * @cfg {Boolean} errorMask (true|false) default false
9973      */
9974     errorMask : false,
9975     
9976     /**
9977      * @cfg {Number} maskOffset Default 100
9978      */
9979     maskOffset : 100,
9980     
9981     /**
9982      * @cfg {Boolean} maskBody
9983      */
9984     maskBody : false,
9985
9986     getAutoCreate : function(){
9987
9988         var cfg = {
9989             tag: 'form',
9990             method : this.method || 'POST',
9991             id : this.id || Roo.id(),
9992             cls : ''
9993         };
9994         if (this.parent().xtype.match(/^Nav/)) {
9995             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9996
9997         }
9998
9999         if (this.labelAlign == 'left' ) {
10000             cfg.cls += ' form-horizontal';
10001         }
10002
10003
10004         return cfg;
10005     },
10006     initEvents : function()
10007     {
10008         this.el.on('submit', this.onSubmit, this);
10009         // this was added as random key presses on the form where triggering form submit.
10010         this.el.on('keypress', function(e) {
10011             if (e.getCharCode() != 13) {
10012                 return true;
10013             }
10014             // we might need to allow it for textareas.. and some other items.
10015             // check e.getTarget().
10016
10017             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10018                 return true;
10019             }
10020
10021             Roo.log("keypress blocked");
10022
10023             e.preventDefault();
10024             return false;
10025         });
10026         
10027     },
10028     // private
10029     onSubmit : function(e){
10030         e.stopEvent();
10031     },
10032
10033      /**
10034      * Returns true if client-side validation on the form is successful.
10035      * @return Boolean
10036      */
10037     isValid : function(){
10038         var items = this.getItems();
10039         var valid = true;
10040         var target = false;
10041         
10042         items.each(function(f){
10043             
10044             if(f.validate()){
10045                 return;
10046             }
10047             
10048             Roo.log('invalid field: ' + f.name);
10049             
10050             valid = false;
10051
10052             if(!target && f.el.isVisible(true)){
10053                 target = f;
10054             }
10055            
10056         });
10057         
10058         if(this.errorMask && !valid){
10059             Roo.bootstrap.Form.popover.mask(this, target);
10060         }
10061         
10062         return valid;
10063     },
10064     
10065     /**
10066      * Returns true if any fields in this form have changed since their original load.
10067      * @return Boolean
10068      */
10069     isDirty : function(){
10070         var dirty = false;
10071         var items = this.getItems();
10072         items.each(function(f){
10073            if(f.isDirty()){
10074                dirty = true;
10075                return false;
10076            }
10077            return true;
10078         });
10079         return dirty;
10080     },
10081      /**
10082      * Performs a predefined action (submit or load) or custom actions you define on this form.
10083      * @param {String} actionName The name of the action type
10084      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10085      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10086      * accept other config options):
10087      * <pre>
10088 Property          Type             Description
10089 ----------------  ---------------  ----------------------------------------------------------------------------------
10090 url               String           The url for the action (defaults to the form's url)
10091 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10092 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10093 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10094                                    validate the form on the client (defaults to false)
10095      * </pre>
10096      * @return {BasicForm} this
10097      */
10098     doAction : function(action, options){
10099         if(typeof action == 'string'){
10100             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10101         }
10102         if(this.fireEvent('beforeaction', this, action) !== false){
10103             this.beforeAction(action);
10104             action.run.defer(100, action);
10105         }
10106         return this;
10107     },
10108
10109     // private
10110     beforeAction : function(action){
10111         var o = action.options;
10112         
10113         if(this.loadMask){
10114             
10115             if(this.maskBody){
10116                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10117             } else {
10118                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10119             }
10120         }
10121         // not really supported yet.. ??
10122
10123         //if(this.waitMsgTarget === true){
10124         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10125         //}else if(this.waitMsgTarget){
10126         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10127         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10128         //}else {
10129         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10130        // }
10131
10132     },
10133
10134     // private
10135     afterAction : function(action, success){
10136         this.activeAction = null;
10137         var o = action.options;
10138
10139         if(this.loadMask){
10140             
10141             if(this.maskBody){
10142                 Roo.get(document.body).unmask();
10143             } else {
10144                 this.el.unmask();
10145             }
10146         }
10147         
10148         //if(this.waitMsgTarget === true){
10149 //            this.el.unmask();
10150         //}else if(this.waitMsgTarget){
10151         //    this.waitMsgTarget.unmask();
10152         //}else{
10153         //    Roo.MessageBox.updateProgress(1);
10154         //    Roo.MessageBox.hide();
10155        // }
10156         //
10157         if(success){
10158             if(o.reset){
10159                 this.reset();
10160             }
10161             Roo.callback(o.success, o.scope, [this, action]);
10162             this.fireEvent('actioncomplete', this, action);
10163
10164         }else{
10165
10166             // failure condition..
10167             // we have a scenario where updates need confirming.
10168             // eg. if a locking scenario exists..
10169             // we look for { errors : { needs_confirm : true }} in the response.
10170             if (
10171                 (typeof(action.result) != 'undefined')  &&
10172                 (typeof(action.result.errors) != 'undefined')  &&
10173                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10174            ){
10175                 var _t = this;
10176                 Roo.log("not supported yet");
10177                  /*
10178
10179                 Roo.MessageBox.confirm(
10180                     "Change requires confirmation",
10181                     action.result.errorMsg,
10182                     function(r) {
10183                         if (r != 'yes') {
10184                             return;
10185                         }
10186                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10187                     }
10188
10189                 );
10190                 */
10191
10192
10193                 return;
10194             }
10195
10196             Roo.callback(o.failure, o.scope, [this, action]);
10197             // show an error message if no failed handler is set..
10198             if (!this.hasListener('actionfailed')) {
10199                 Roo.log("need to add dialog support");
10200                 /*
10201                 Roo.MessageBox.alert("Error",
10202                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10203                         action.result.errorMsg :
10204                         "Saving Failed, please check your entries or try again"
10205                 );
10206                 */
10207             }
10208
10209             this.fireEvent('actionfailed', this, action);
10210         }
10211
10212     },
10213     /**
10214      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10215      * @param {String} id The value to search for
10216      * @return Field
10217      */
10218     findField : function(id){
10219         var items = this.getItems();
10220         var field = items.get(id);
10221         if(!field){
10222              items.each(function(f){
10223                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10224                     field = f;
10225                     return false;
10226                 }
10227                 return true;
10228             });
10229         }
10230         return field || null;
10231     },
10232      /**
10233      * Mark fields in this form invalid in bulk.
10234      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10235      * @return {BasicForm} this
10236      */
10237     markInvalid : function(errors){
10238         if(errors instanceof Array){
10239             for(var i = 0, len = errors.length; i < len; i++){
10240                 var fieldError = errors[i];
10241                 var f = this.findField(fieldError.id);
10242                 if(f){
10243                     f.markInvalid(fieldError.msg);
10244                 }
10245             }
10246         }else{
10247             var field, id;
10248             for(id in errors){
10249                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10250                     field.markInvalid(errors[id]);
10251                 }
10252             }
10253         }
10254         //Roo.each(this.childForms || [], function (f) {
10255         //    f.markInvalid(errors);
10256         //});
10257
10258         return this;
10259     },
10260
10261     /**
10262      * Set values for fields in this form in bulk.
10263      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10264      * @return {BasicForm} this
10265      */
10266     setValues : function(values){
10267         if(values instanceof Array){ // array of objects
10268             for(var i = 0, len = values.length; i < len; i++){
10269                 var v = values[i];
10270                 var f = this.findField(v.id);
10271                 if(f){
10272                     f.setValue(v.value);
10273                     if(this.trackResetOnLoad){
10274                         f.originalValue = f.getValue();
10275                     }
10276                 }
10277             }
10278         }else{ // object hash
10279             var field, id;
10280             for(id in values){
10281                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10282
10283                     if (field.setFromData &&
10284                         field.valueField &&
10285                         field.displayField &&
10286                         // combos' with local stores can
10287                         // be queried via setValue()
10288                         // to set their value..
10289                         (field.store && !field.store.isLocal)
10290                         ) {
10291                         // it's a combo
10292                         var sd = { };
10293                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10294                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10295                         field.setFromData(sd);
10296
10297                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10298                         
10299                         field.setFromData(values);
10300                         
10301                     } else {
10302                         field.setValue(values[id]);
10303                     }
10304
10305
10306                     if(this.trackResetOnLoad){
10307                         field.originalValue = field.getValue();
10308                     }
10309                 }
10310             }
10311         }
10312
10313         //Roo.each(this.childForms || [], function (f) {
10314         //    f.setValues(values);
10315         //});
10316
10317         return this;
10318     },
10319
10320     /**
10321      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10322      * they are returned as an array.
10323      * @param {Boolean} asString
10324      * @return {Object}
10325      */
10326     getValues : function(asString){
10327         //if (this.childForms) {
10328             // copy values from the child forms
10329         //    Roo.each(this.childForms, function (f) {
10330         //        this.setValues(f.getValues());
10331         //    }, this);
10332         //}
10333
10334
10335
10336         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10337         if(asString === true){
10338             return fs;
10339         }
10340         return Roo.urlDecode(fs);
10341     },
10342
10343     /**
10344      * Returns the fields in this form as an object with key/value pairs.
10345      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10346      * @return {Object}
10347      */
10348     getFieldValues : function(with_hidden)
10349     {
10350         var items = this.getItems();
10351         var ret = {};
10352         items.each(function(f){
10353             
10354             if (!f.getName()) {
10355                 return;
10356             }
10357             
10358             var v = f.getValue();
10359             
10360             if (f.inputType =='radio') {
10361                 if (typeof(ret[f.getName()]) == 'undefined') {
10362                     ret[f.getName()] = ''; // empty..
10363                 }
10364
10365                 if (!f.el.dom.checked) {
10366                     return;
10367
10368                 }
10369                 v = f.el.dom.value;
10370
10371             }
10372             
10373             if(f.xtype == 'MoneyField'){
10374                 ret[f.currencyName] = f.getCurrency();
10375             }
10376
10377             // not sure if this supported any more..
10378             if ((typeof(v) == 'object') && f.getRawValue) {
10379                 v = f.getRawValue() ; // dates..
10380             }
10381             // combo boxes where name != hiddenName...
10382             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10383                 ret[f.name] = f.getRawValue();
10384             }
10385             ret[f.getName()] = v;
10386         });
10387
10388         return ret;
10389     },
10390
10391     /**
10392      * Clears all invalid messages in this form.
10393      * @return {BasicForm} this
10394      */
10395     clearInvalid : function(){
10396         var items = this.getItems();
10397
10398         items.each(function(f){
10399            f.clearInvalid();
10400         });
10401
10402         return this;
10403     },
10404
10405     /**
10406      * Resets this form.
10407      * @return {BasicForm} this
10408      */
10409     reset : function(){
10410         var items = this.getItems();
10411         items.each(function(f){
10412             f.reset();
10413         });
10414
10415         Roo.each(this.childForms || [], function (f) {
10416             f.reset();
10417         });
10418
10419
10420         return this;
10421     },
10422     
10423     getItems : function()
10424     {
10425         var r=new Roo.util.MixedCollection(false, function(o){
10426             return o.id || (o.id = Roo.id());
10427         });
10428         var iter = function(el) {
10429             if (el.inputEl) {
10430                 r.add(el);
10431             }
10432             if (!el.items) {
10433                 return;
10434             }
10435             Roo.each(el.items,function(e) {
10436                 iter(e);
10437             });
10438         };
10439
10440         iter(this);
10441         return r;
10442     },
10443     
10444     hideFields : function(items)
10445     {
10446         Roo.each(items, function(i){
10447             
10448             var f = this.findField(i);
10449             
10450             if(!f){
10451                 return;
10452             }
10453             
10454             f.hide();
10455             
10456         }, this);
10457     },
10458     
10459     showFields : function(items)
10460     {
10461         Roo.each(items, function(i){
10462             
10463             var f = this.findField(i);
10464             
10465             if(!f){
10466                 return;
10467             }
10468             
10469             f.show();
10470             
10471         }, this);
10472     }
10473
10474 });
10475
10476 Roo.apply(Roo.bootstrap.Form, {
10477     
10478     popover : {
10479         
10480         padding : 5,
10481         
10482         isApplied : false,
10483         
10484         isMasked : false,
10485         
10486         form : false,
10487         
10488         target : false,
10489         
10490         toolTip : false,
10491         
10492         intervalID : false,
10493         
10494         maskEl : false,
10495         
10496         apply : function()
10497         {
10498             if(this.isApplied){
10499                 return;
10500             }
10501             
10502             this.maskEl = {
10503                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10504                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10505                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10506                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10507             };
10508             
10509             this.maskEl.top.enableDisplayMode("block");
10510             this.maskEl.left.enableDisplayMode("block");
10511             this.maskEl.bottom.enableDisplayMode("block");
10512             this.maskEl.right.enableDisplayMode("block");
10513             
10514             this.toolTip = new Roo.bootstrap.Tooltip({
10515                 cls : 'roo-form-error-popover',
10516                 alignment : {
10517                     'left' : ['r-l', [-2,0], 'right'],
10518                     'right' : ['l-r', [2,0], 'left'],
10519                     'bottom' : ['tl-bl', [0,2], 'top'],
10520                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10521                 }
10522             });
10523             
10524             this.toolTip.render(Roo.get(document.body));
10525
10526             this.toolTip.el.enableDisplayMode("block");
10527             
10528             Roo.get(document.body).on('click', function(){
10529                 this.unmask();
10530             }, this);
10531             
10532             Roo.get(document.body).on('touchstart', function(){
10533                 this.unmask();
10534             }, this);
10535             
10536             this.isApplied = true
10537         },
10538         
10539         mask : function(form, target)
10540         {
10541             this.form = form;
10542             
10543             this.target = target;
10544             
10545             if(!this.form.errorMask || !target.el){
10546                 return;
10547             }
10548             
10549             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10550             
10551             Roo.log(scrollable);
10552             
10553             var ot = this.target.el.calcOffsetsTo(scrollable);
10554             
10555             var scrollTo = ot[1] - this.form.maskOffset;
10556             
10557             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10558             
10559             scrollable.scrollTo('top', scrollTo);
10560             
10561             var box = this.target.el.getBox();
10562             Roo.log(box);
10563             var zIndex = Roo.bootstrap.Modal.zIndex++;
10564
10565             
10566             this.maskEl.top.setStyle('position', 'absolute');
10567             this.maskEl.top.setStyle('z-index', zIndex);
10568             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10569             this.maskEl.top.setLeft(0);
10570             this.maskEl.top.setTop(0);
10571             this.maskEl.top.show();
10572             
10573             this.maskEl.left.setStyle('position', 'absolute');
10574             this.maskEl.left.setStyle('z-index', zIndex);
10575             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10576             this.maskEl.left.setLeft(0);
10577             this.maskEl.left.setTop(box.y - this.padding);
10578             this.maskEl.left.show();
10579
10580             this.maskEl.bottom.setStyle('position', 'absolute');
10581             this.maskEl.bottom.setStyle('z-index', zIndex);
10582             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10583             this.maskEl.bottom.setLeft(0);
10584             this.maskEl.bottom.setTop(box.bottom + this.padding);
10585             this.maskEl.bottom.show();
10586
10587             this.maskEl.right.setStyle('position', 'absolute');
10588             this.maskEl.right.setStyle('z-index', zIndex);
10589             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10590             this.maskEl.right.setLeft(box.right + this.padding);
10591             this.maskEl.right.setTop(box.y - this.padding);
10592             this.maskEl.right.show();
10593
10594             this.toolTip.bindEl = this.target.el;
10595
10596             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10597
10598             var tip = this.target.blankText;
10599
10600             if(this.target.getValue() !== '' ) {
10601                 
10602                 if (this.target.invalidText.length) {
10603                     tip = this.target.invalidText;
10604                 } else if (this.target.regexText.length){
10605                     tip = this.target.regexText;
10606                 }
10607             }
10608
10609             this.toolTip.show(tip);
10610
10611             this.intervalID = window.setInterval(function() {
10612                 Roo.bootstrap.Form.popover.unmask();
10613             }, 10000);
10614
10615             window.onwheel = function(){ return false;};
10616             
10617             (function(){ this.isMasked = true; }).defer(500, this);
10618             
10619         },
10620         
10621         unmask : function()
10622         {
10623             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10624                 return;
10625             }
10626             
10627             this.maskEl.top.setStyle('position', 'absolute');
10628             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10629             this.maskEl.top.hide();
10630
10631             this.maskEl.left.setStyle('position', 'absolute');
10632             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10633             this.maskEl.left.hide();
10634
10635             this.maskEl.bottom.setStyle('position', 'absolute');
10636             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10637             this.maskEl.bottom.hide();
10638
10639             this.maskEl.right.setStyle('position', 'absolute');
10640             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10641             this.maskEl.right.hide();
10642             
10643             this.toolTip.hide();
10644             
10645             this.toolTip.el.hide();
10646             
10647             window.onwheel = function(){ return true;};
10648             
10649             if(this.intervalID){
10650                 window.clearInterval(this.intervalID);
10651                 this.intervalID = false;
10652             }
10653             
10654             this.isMasked = false;
10655             
10656         }
10657         
10658     }
10659     
10660 });
10661
10662 /*
10663  * Based on:
10664  * Ext JS Library 1.1.1
10665  * Copyright(c) 2006-2007, Ext JS, LLC.
10666  *
10667  * Originally Released Under LGPL - original licence link has changed is not relivant.
10668  *
10669  * Fork - LGPL
10670  * <script type="text/javascript">
10671  */
10672 /**
10673  * @class Roo.form.VTypes
10674  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10675  * @singleton
10676  */
10677 Roo.form.VTypes = function(){
10678     // closure these in so they are only created once.
10679     var alpha = /^[a-zA-Z_]+$/;
10680     var alphanum = /^[a-zA-Z0-9_]+$/;
10681     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10682     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10683
10684     // All these messages and functions are configurable
10685     return {
10686         /**
10687          * The function used to validate email addresses
10688          * @param {String} value The email address
10689          */
10690         'email' : function(v){
10691             return email.test(v);
10692         },
10693         /**
10694          * The error text to display when the email validation function returns false
10695          * @type String
10696          */
10697         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10698         /**
10699          * The keystroke filter mask to be applied on email input
10700          * @type RegExp
10701          */
10702         'emailMask' : /[a-z0-9_\.\-@]/i,
10703
10704         /**
10705          * The function used to validate URLs
10706          * @param {String} value The URL
10707          */
10708         'url' : function(v){
10709             return url.test(v);
10710         },
10711         /**
10712          * The error text to display when the url validation function returns false
10713          * @type String
10714          */
10715         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10716         
10717         /**
10718          * The function used to validate alpha values
10719          * @param {String} value The value
10720          */
10721         'alpha' : function(v){
10722             return alpha.test(v);
10723         },
10724         /**
10725          * The error text to display when the alpha validation function returns false
10726          * @type String
10727          */
10728         'alphaText' : 'This field should only contain letters and _',
10729         /**
10730          * The keystroke filter mask to be applied on alpha input
10731          * @type RegExp
10732          */
10733         'alphaMask' : /[a-z_]/i,
10734
10735         /**
10736          * The function used to validate alphanumeric values
10737          * @param {String} value The value
10738          */
10739         'alphanum' : function(v){
10740             return alphanum.test(v);
10741         },
10742         /**
10743          * The error text to display when the alphanumeric validation function returns false
10744          * @type String
10745          */
10746         'alphanumText' : 'This field should only contain letters, numbers and _',
10747         /**
10748          * The keystroke filter mask to be applied on alphanumeric input
10749          * @type RegExp
10750          */
10751         'alphanumMask' : /[a-z0-9_]/i
10752     };
10753 }();/*
10754  * - LGPL
10755  *
10756  * Input
10757  * 
10758  */
10759
10760 /**
10761  * @class Roo.bootstrap.Input
10762  * @extends Roo.bootstrap.Component
10763  * Bootstrap Input class
10764  * @cfg {Boolean} disabled is it disabled
10765  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10766  * @cfg {String} name name of the input
10767  * @cfg {string} fieldLabel - the label associated
10768  * @cfg {string} placeholder - placeholder to put in text.
10769  * @cfg {string}  before - input group add on before
10770  * @cfg {string} after - input group add on after
10771  * @cfg {string} size - (lg|sm) or leave empty..
10772  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10773  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10774  * @cfg {Number} md colspan out of 12 for computer-sized screens
10775  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10776  * @cfg {string} value default value of the input
10777  * @cfg {Number} labelWidth set the width of label 
10778  * @cfg {Number} labellg set the width of label (1-12)
10779  * @cfg {Number} labelmd set the width of label (1-12)
10780  * @cfg {Number} labelsm set the width of label (1-12)
10781  * @cfg {Number} labelxs set the width of label (1-12)
10782  * @cfg {String} labelAlign (top|left)
10783  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10784  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10785  * @cfg {String} indicatorpos (left|right) default left
10786  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10787  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10788  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10789
10790  * @cfg {String} align (left|center|right) Default left
10791  * @cfg {Boolean} forceFeedback (true|false) Default false
10792  * 
10793  * @constructor
10794  * Create a new Input
10795  * @param {Object} config The config object
10796  */
10797
10798 Roo.bootstrap.Input = function(config){
10799     
10800     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10801     
10802     this.addEvents({
10803         /**
10804          * @event focus
10805          * Fires when this field receives input focus.
10806          * @param {Roo.form.Field} this
10807          */
10808         focus : true,
10809         /**
10810          * @event blur
10811          * Fires when this field loses input focus.
10812          * @param {Roo.form.Field} this
10813          */
10814         blur : true,
10815         /**
10816          * @event specialkey
10817          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10818          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10819          * @param {Roo.form.Field} this
10820          * @param {Roo.EventObject} e The event object
10821          */
10822         specialkey : true,
10823         /**
10824          * @event change
10825          * Fires just before the field blurs if the field value has changed.
10826          * @param {Roo.form.Field} this
10827          * @param {Mixed} newValue The new value
10828          * @param {Mixed} oldValue The original value
10829          */
10830         change : true,
10831         /**
10832          * @event invalid
10833          * Fires after the field has been marked as invalid.
10834          * @param {Roo.form.Field} this
10835          * @param {String} msg The validation message
10836          */
10837         invalid : true,
10838         /**
10839          * @event valid
10840          * Fires after the field has been validated with no errors.
10841          * @param {Roo.form.Field} this
10842          */
10843         valid : true,
10844          /**
10845          * @event keyup
10846          * Fires after the key up
10847          * @param {Roo.form.Field} this
10848          * @param {Roo.EventObject}  e The event Object
10849          */
10850         keyup : true,
10851         /**
10852          * @event paste
10853          * Fires after the user pastes into input
10854          * @param {Roo.form.Field} this
10855          * @param {Roo.EventObject}  e The event Object
10856          */
10857         paste : true
10858     });
10859 };
10860
10861 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10862      /**
10863      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10864       automatic validation (defaults to "keyup").
10865      */
10866     validationEvent : "keyup",
10867      /**
10868      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10869      */
10870     validateOnBlur : true,
10871     /**
10872      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10873      */
10874     validationDelay : 250,
10875      /**
10876      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10877      */
10878     focusClass : "x-form-focus",  // not needed???
10879     
10880        
10881     /**
10882      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10883      */
10884     invalidClass : "has-warning",
10885     
10886     /**
10887      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10888      */
10889     validClass : "has-success",
10890     
10891     /**
10892      * @cfg {Boolean} hasFeedback (true|false) default true
10893      */
10894     hasFeedback : true,
10895     
10896     /**
10897      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10898      */
10899     invalidFeedbackClass : "glyphicon-warning-sign",
10900     
10901     /**
10902      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10903      */
10904     validFeedbackClass : "glyphicon-ok",
10905     
10906     /**
10907      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10908      */
10909     selectOnFocus : false,
10910     
10911      /**
10912      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10913      */
10914     maskRe : null,
10915        /**
10916      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10917      */
10918     vtype : null,
10919     
10920       /**
10921      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10922      */
10923     disableKeyFilter : false,
10924     
10925        /**
10926      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10927      */
10928     disabled : false,
10929      /**
10930      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10931      */
10932     allowBlank : true,
10933     /**
10934      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10935      */
10936     blankText : "Please complete this mandatory field",
10937     
10938      /**
10939      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10940      */
10941     minLength : 0,
10942     /**
10943      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10944      */
10945     maxLength : Number.MAX_VALUE,
10946     /**
10947      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10948      */
10949     minLengthText : "The minimum length for this field is {0}",
10950     /**
10951      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10952      */
10953     maxLengthText : "The maximum length for this field is {0}",
10954   
10955     
10956     /**
10957      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10958      * If available, this function will be called only after the basic validators all return true, and will be passed the
10959      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10960      */
10961     validator : null,
10962     /**
10963      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10964      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10965      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10966      */
10967     regex : null,
10968     /**
10969      * @cfg {String} regexText -- Depricated - use Invalid Text
10970      */
10971     regexText : "",
10972     
10973     /**
10974      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10975      */
10976     invalidText : "",
10977     
10978     
10979     
10980     autocomplete: false,
10981     
10982     
10983     fieldLabel : '',
10984     inputType : 'text',
10985     
10986     name : false,
10987     placeholder: false,
10988     before : false,
10989     after : false,
10990     size : false,
10991     hasFocus : false,
10992     preventMark: false,
10993     isFormField : true,
10994     value : '',
10995     labelWidth : 2,
10996     labelAlign : false,
10997     readOnly : false,
10998     align : false,
10999     formatedValue : false,
11000     forceFeedback : false,
11001     
11002     indicatorpos : 'left',
11003     
11004     labellg : 0,
11005     labelmd : 0,
11006     labelsm : 0,
11007     labelxs : 0,
11008     
11009     capture : '',
11010     accept : '',
11011     
11012     parentLabelAlign : function()
11013     {
11014         var parent = this;
11015         while (parent.parent()) {
11016             parent = parent.parent();
11017             if (typeof(parent.labelAlign) !='undefined') {
11018                 return parent.labelAlign;
11019             }
11020         }
11021         return 'left';
11022         
11023     },
11024     
11025     getAutoCreate : function()
11026     {
11027         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11028         
11029         var id = Roo.id();
11030         
11031         var cfg = {};
11032         
11033         if(this.inputType != 'hidden'){
11034             cfg.cls = 'form-group' //input-group
11035         }
11036         
11037         var input =  {
11038             tag: 'input',
11039             id : id,
11040             type : this.inputType,
11041             value : this.value,
11042             cls : 'form-control',
11043             placeholder : this.placeholder || '',
11044             autocomplete : this.autocomplete || 'new-password'
11045         };
11046         if (this.inputType == 'file') {
11047             input.style = 'overflow:hidden'; // why not in CSS?
11048         }
11049         
11050         if(this.capture.length){
11051             input.capture = this.capture;
11052         }
11053         
11054         if(this.accept.length){
11055             input.accept = this.accept + "/*";
11056         }
11057         
11058         if(this.align){
11059             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11060         }
11061         
11062         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11063             input.maxLength = this.maxLength;
11064         }
11065         
11066         if (this.disabled) {
11067             input.disabled=true;
11068         }
11069         
11070         if (this.readOnly) {
11071             input.readonly=true;
11072         }
11073         
11074         if (this.name) {
11075             input.name = this.name;
11076         }
11077         
11078         if (this.size) {
11079             input.cls += ' input-' + this.size;
11080         }
11081         
11082         var settings=this;
11083         ['xs','sm','md','lg'].map(function(size){
11084             if (settings[size]) {
11085                 cfg.cls += ' col-' + size + '-' + settings[size];
11086             }
11087         });
11088         
11089         var inputblock = input;
11090         
11091         var feedback = {
11092             tag: 'span',
11093             cls: 'glyphicon form-control-feedback'
11094         };
11095             
11096         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11097             
11098             inputblock = {
11099                 cls : 'has-feedback',
11100                 cn :  [
11101                     input,
11102                     feedback
11103                 ] 
11104             };  
11105         }
11106         
11107         if (this.before || this.after) {
11108             
11109             inputblock = {
11110                 cls : 'input-group',
11111                 cn :  [] 
11112             };
11113             
11114             if (this.before && typeof(this.before) == 'string') {
11115                 
11116                 inputblock.cn.push({
11117                     tag :'span',
11118                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11119                     html : this.before
11120                 });
11121             }
11122             if (this.before && typeof(this.before) == 'object') {
11123                 this.before = Roo.factory(this.before);
11124                 
11125                 inputblock.cn.push({
11126                     tag :'span',
11127                     cls : 'roo-input-before input-group-prepend   input-group-' +
11128                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11129                 });
11130             }
11131             
11132             inputblock.cn.push(input);
11133             
11134             if (this.after && typeof(this.after) == 'string') {
11135                 inputblock.cn.push({
11136                     tag :'span',
11137                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11138                     html : this.after
11139                 });
11140             }
11141             if (this.after && typeof(this.after) == 'object') {
11142                 this.after = Roo.factory(this.after);
11143                 
11144                 inputblock.cn.push({
11145                     tag :'span',
11146                     cls : 'roo-input-after input-group-append  input-group-' +
11147                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11148                 });
11149             }
11150             
11151             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11152                 inputblock.cls += ' has-feedback';
11153                 inputblock.cn.push(feedback);
11154             }
11155         };
11156         var indicator = {
11157             tag : 'i',
11158             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11159             tooltip : 'This field is required'
11160         };
11161         if (this.allowBlank ) {
11162             indicator.style = this.allowBlank ? ' display:none' : '';
11163         }
11164         if (align ==='left' && this.fieldLabel.length) {
11165             
11166             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11167             
11168             cfg.cn = [
11169                 indicator,
11170                 {
11171                     tag: 'label',
11172                     'for' :  id,
11173                     cls : 'control-label col-form-label',
11174                     html : this.fieldLabel
11175
11176                 },
11177                 {
11178                     cls : "", 
11179                     cn: [
11180                         inputblock
11181                     ]
11182                 }
11183             ];
11184             
11185             var labelCfg = cfg.cn[1];
11186             var contentCfg = cfg.cn[2];
11187             
11188             if(this.indicatorpos == 'right'){
11189                 cfg.cn = [
11190                     {
11191                         tag: 'label',
11192                         'for' :  id,
11193                         cls : 'control-label col-form-label',
11194                         cn : [
11195                             {
11196                                 tag : 'span',
11197                                 html : this.fieldLabel
11198                             },
11199                             indicator
11200                         ]
11201                     },
11202                     {
11203                         cls : "",
11204                         cn: [
11205                             inputblock
11206                         ]
11207                     }
11208
11209                 ];
11210                 
11211                 labelCfg = cfg.cn[0];
11212                 contentCfg = cfg.cn[1];
11213             
11214             }
11215             
11216             if(this.labelWidth > 12){
11217                 labelCfg.style = "width: " + this.labelWidth + 'px';
11218             }
11219             
11220             if(this.labelWidth < 13 && this.labelmd == 0){
11221                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11222             }
11223             
11224             if(this.labellg > 0){
11225                 labelCfg.cls += ' col-lg-' + this.labellg;
11226                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11227             }
11228             
11229             if(this.labelmd > 0){
11230                 labelCfg.cls += ' col-md-' + this.labelmd;
11231                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11232             }
11233             
11234             if(this.labelsm > 0){
11235                 labelCfg.cls += ' col-sm-' + this.labelsm;
11236                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11237             }
11238             
11239             if(this.labelxs > 0){
11240                 labelCfg.cls += ' col-xs-' + this.labelxs;
11241                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11242             }
11243             
11244             
11245         } else if ( this.fieldLabel.length) {
11246                 
11247             
11248             
11249             cfg.cn = [
11250                 {
11251                     tag : 'i',
11252                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11253                     tooltip : 'This field is required',
11254                     style : this.allowBlank ? ' display:none' : '' 
11255                 },
11256                 {
11257                     tag: 'label',
11258                    //cls : 'input-group-addon',
11259                     html : this.fieldLabel
11260
11261                 },
11262
11263                inputblock
11264
11265            ];
11266            
11267            if(this.indicatorpos == 'right'){
11268        
11269                 cfg.cn = [
11270                     {
11271                         tag: 'label',
11272                        //cls : 'input-group-addon',
11273                         html : this.fieldLabel
11274
11275                     },
11276                     {
11277                         tag : 'i',
11278                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11279                         tooltip : 'This field is required',
11280                         style : this.allowBlank ? ' display:none' : '' 
11281                     },
11282
11283                    inputblock
11284
11285                ];
11286
11287             }
11288
11289         } else {
11290             
11291             cfg.cn = [
11292
11293                     inputblock
11294
11295             ];
11296                 
11297                 
11298         };
11299         
11300         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11301            cfg.cls += ' navbar-form';
11302         }
11303         
11304         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11305             // on BS4 we do this only if not form 
11306             cfg.cls += ' navbar-form';
11307             cfg.tag = 'li';
11308         }
11309         
11310         return cfg;
11311         
11312     },
11313     /**
11314      * return the real input element.
11315      */
11316     inputEl: function ()
11317     {
11318         return this.el.select('input.form-control',true).first();
11319     },
11320     
11321     tooltipEl : function()
11322     {
11323         return this.inputEl();
11324     },
11325     
11326     indicatorEl : function()
11327     {
11328         if (Roo.bootstrap.version == 4) {
11329             return false; // not enabled in v4 yet.
11330         }
11331         
11332         var indicator = this.el.select('i.roo-required-indicator',true).first();
11333         
11334         if(!indicator){
11335             return false;
11336         }
11337         
11338         return indicator;
11339         
11340     },
11341     
11342     setDisabled : function(v)
11343     {
11344         var i  = this.inputEl().dom;
11345         if (!v) {
11346             i.removeAttribute('disabled');
11347             return;
11348             
11349         }
11350         i.setAttribute('disabled','true');
11351     },
11352     initEvents : function()
11353     {
11354           
11355         this.inputEl().on("keydown" , this.fireKey,  this);
11356         this.inputEl().on("focus", this.onFocus,  this);
11357         this.inputEl().on("blur", this.onBlur,  this);
11358         
11359         this.inputEl().relayEvent('keyup', this);
11360         this.inputEl().relayEvent('paste', this);
11361         
11362         this.indicator = this.indicatorEl();
11363         
11364         if(this.indicator){
11365             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11366         }
11367  
11368         // reference to original value for reset
11369         this.originalValue = this.getValue();
11370         //Roo.form.TextField.superclass.initEvents.call(this);
11371         if(this.validationEvent == 'keyup'){
11372             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11373             this.inputEl().on('keyup', this.filterValidation, this);
11374         }
11375         else if(this.validationEvent !== false){
11376             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11377         }
11378         
11379         if(this.selectOnFocus){
11380             this.on("focus", this.preFocus, this);
11381             
11382         }
11383         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11384             this.inputEl().on("keypress", this.filterKeys, this);
11385         } else {
11386             this.inputEl().relayEvent('keypress', this);
11387         }
11388        /* if(this.grow){
11389             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11390             this.el.on("click", this.autoSize,  this);
11391         }
11392         */
11393         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11394             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11395         }
11396         
11397         if (typeof(this.before) == 'object') {
11398             this.before.render(this.el.select('.roo-input-before',true).first());
11399         }
11400         if (typeof(this.after) == 'object') {
11401             this.after.render(this.el.select('.roo-input-after',true).first());
11402         }
11403         
11404         this.inputEl().on('change', this.onChange, this);
11405         
11406     },
11407     filterValidation : function(e){
11408         if(!e.isNavKeyPress()){
11409             this.validationTask.delay(this.validationDelay);
11410         }
11411     },
11412      /**
11413      * Validates the field value
11414      * @return {Boolean} True if the value is valid, else false
11415      */
11416     validate : function(){
11417         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11418         if(this.disabled || this.validateValue(this.getRawValue())){
11419             this.markValid();
11420             return true;
11421         }
11422         
11423         this.markInvalid();
11424         return false;
11425     },
11426     
11427     
11428     /**
11429      * Validates a value according to the field's validation rules and marks the field as invalid
11430      * if the validation fails
11431      * @param {Mixed} value The value to validate
11432      * @return {Boolean} True if the value is valid, else false
11433      */
11434     validateValue : function(value)
11435     {
11436         if(this.getVisibilityEl().hasClass('hidden')){
11437             return true;
11438         }
11439         
11440         if(value.length < 1)  { // if it's blank
11441             if(this.allowBlank){
11442                 return true;
11443             }
11444             return false;
11445         }
11446         
11447         if(value.length < this.minLength){
11448             return false;
11449         }
11450         if(value.length > this.maxLength){
11451             return false;
11452         }
11453         if(this.vtype){
11454             var vt = Roo.form.VTypes;
11455             if(!vt[this.vtype](value, this)){
11456                 return false;
11457             }
11458         }
11459         if(typeof this.validator == "function"){
11460             var msg = this.validator(value);
11461             if(msg !== true){
11462                 return false;
11463             }
11464             if (typeof(msg) == 'string') {
11465                 this.invalidText = msg;
11466             }
11467         }
11468         
11469         if(this.regex && !this.regex.test(value)){
11470             return false;
11471         }
11472         
11473         return true;
11474     },
11475     
11476      // private
11477     fireKey : function(e){
11478         //Roo.log('field ' + e.getKey());
11479         if(e.isNavKeyPress()){
11480             this.fireEvent("specialkey", this, e);
11481         }
11482     },
11483     focus : function (selectText){
11484         if(this.rendered){
11485             this.inputEl().focus();
11486             if(selectText === true){
11487                 this.inputEl().dom.select();
11488             }
11489         }
11490         return this;
11491     } ,
11492     
11493     onFocus : function(){
11494         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11495            // this.el.addClass(this.focusClass);
11496         }
11497         if(!this.hasFocus){
11498             this.hasFocus = true;
11499             this.startValue = this.getValue();
11500             this.fireEvent("focus", this);
11501         }
11502     },
11503     
11504     beforeBlur : Roo.emptyFn,
11505
11506     
11507     // private
11508     onBlur : function(){
11509         this.beforeBlur();
11510         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11511             //this.el.removeClass(this.focusClass);
11512         }
11513         this.hasFocus = false;
11514         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11515             this.validate();
11516         }
11517         var v = this.getValue();
11518         if(String(v) !== String(this.startValue)){
11519             this.fireEvent('change', this, v, this.startValue);
11520         }
11521         this.fireEvent("blur", this);
11522     },
11523     
11524     onChange : function(e)
11525     {
11526         var v = this.getValue();
11527         if(String(v) !== String(this.startValue)){
11528             this.fireEvent('change', this, v, this.startValue);
11529         }
11530         
11531     },
11532     
11533     /**
11534      * Resets the current field value to the originally loaded value and clears any validation messages
11535      */
11536     reset : function(){
11537         this.setValue(this.originalValue);
11538         this.validate();
11539     },
11540      /**
11541      * Returns the name of the field
11542      * @return {Mixed} name The name field
11543      */
11544     getName: function(){
11545         return this.name;
11546     },
11547      /**
11548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11549      * @return {Mixed} value The field value
11550      */
11551     getValue : function(){
11552         
11553         var v = this.inputEl().getValue();
11554         
11555         return v;
11556     },
11557     /**
11558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11559      * @return {Mixed} value The field value
11560      */
11561     getRawValue : function(){
11562         var v = this.inputEl().getValue();
11563         
11564         return v;
11565     },
11566     
11567     /**
11568      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11569      * @param {Mixed} value The value to set
11570      */
11571     setRawValue : function(v){
11572         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11573     },
11574     
11575     selectText : function(start, end){
11576         var v = this.getRawValue();
11577         if(v.length > 0){
11578             start = start === undefined ? 0 : start;
11579             end = end === undefined ? v.length : end;
11580             var d = this.inputEl().dom;
11581             if(d.setSelectionRange){
11582                 d.setSelectionRange(start, end);
11583             }else if(d.createTextRange){
11584                 var range = d.createTextRange();
11585                 range.moveStart("character", start);
11586                 range.moveEnd("character", v.length-end);
11587                 range.select();
11588             }
11589         }
11590     },
11591     
11592     /**
11593      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11594      * @param {Mixed} value The value to set
11595      */
11596     setValue : function(v){
11597         this.value = v;
11598         if(this.rendered){
11599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11600             this.validate();
11601         }
11602     },
11603     
11604     /*
11605     processValue : function(value){
11606         if(this.stripCharsRe){
11607             var newValue = value.replace(this.stripCharsRe, '');
11608             if(newValue !== value){
11609                 this.setRawValue(newValue);
11610                 return newValue;
11611             }
11612         }
11613         return value;
11614     },
11615   */
11616     preFocus : function(){
11617         
11618         if(this.selectOnFocus){
11619             this.inputEl().dom.select();
11620         }
11621     },
11622     filterKeys : function(e){
11623         var k = e.getKey();
11624         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11625             return;
11626         }
11627         var c = e.getCharCode(), cc = String.fromCharCode(c);
11628         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11629             return;
11630         }
11631         if(!this.maskRe.test(cc)){
11632             e.stopEvent();
11633         }
11634     },
11635      /**
11636      * Clear any invalid styles/messages for this field
11637      */
11638     clearInvalid : function(){
11639         
11640         if(!this.el || this.preventMark){ // not rendered
11641             return;
11642         }
11643         
11644         
11645         this.el.removeClass([this.invalidClass, 'is-invalid']);
11646         
11647         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11648             
11649             var feedback = this.el.select('.form-control-feedback', true).first();
11650             
11651             if(feedback){
11652                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11653             }
11654             
11655         }
11656         
11657         if(this.indicator){
11658             this.indicator.removeClass('visible');
11659             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11660         }
11661         
11662         this.fireEvent('valid', this);
11663     },
11664     
11665      /**
11666      * Mark this field as valid
11667      */
11668     markValid : function()
11669     {
11670         if(!this.el  || this.preventMark){ // not rendered...
11671             return;
11672         }
11673         
11674         this.el.removeClass([this.invalidClass, this.validClass]);
11675         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11676
11677         var feedback = this.el.select('.form-control-feedback', true).first();
11678             
11679         if(feedback){
11680             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11681         }
11682         
11683         if(this.indicator){
11684             this.indicator.removeClass('visible');
11685             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11686         }
11687         
11688         if(this.disabled){
11689             return;
11690         }
11691         
11692            
11693         if(this.allowBlank && !this.getRawValue().length){
11694             return;
11695         }
11696         if (Roo.bootstrap.version == 3) {
11697             this.el.addClass(this.validClass);
11698         } else {
11699             this.inputEl().addClass('is-valid');
11700         }
11701
11702         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11703             
11704             var feedback = this.el.select('.form-control-feedback', true).first();
11705             
11706             if(feedback){
11707                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11708                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11709             }
11710             
11711         }
11712         
11713         this.fireEvent('valid', this);
11714     },
11715     
11716      /**
11717      * Mark this field as invalid
11718      * @param {String} msg The validation message
11719      */
11720     markInvalid : function(msg)
11721     {
11722         if(!this.el  || this.preventMark){ // not rendered
11723             return;
11724         }
11725         
11726         this.el.removeClass([this.invalidClass, this.validClass]);
11727         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11728         
11729         var feedback = this.el.select('.form-control-feedback', true).first();
11730             
11731         if(feedback){
11732             this.el.select('.form-control-feedback', true).first().removeClass(
11733                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11734         }
11735
11736         if(this.disabled){
11737             return;
11738         }
11739         
11740         if(this.allowBlank && !this.getRawValue().length){
11741             return;
11742         }
11743         
11744         if(this.indicator){
11745             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11746             this.indicator.addClass('visible');
11747         }
11748         if (Roo.bootstrap.version == 3) {
11749             this.el.addClass(this.invalidClass);
11750         } else {
11751             this.inputEl().addClass('is-invalid');
11752         }
11753         
11754         
11755         
11756         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11757             
11758             var feedback = this.el.select('.form-control-feedback', true).first();
11759             
11760             if(feedback){
11761                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11762                 
11763                 if(this.getValue().length || this.forceFeedback){
11764                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11765                 }
11766                 
11767             }
11768             
11769         }
11770         
11771         this.fireEvent('invalid', this, msg);
11772     },
11773     // private
11774     SafariOnKeyDown : function(event)
11775     {
11776         // this is a workaround for a password hang bug on chrome/ webkit.
11777         if (this.inputEl().dom.type != 'password') {
11778             return;
11779         }
11780         
11781         var isSelectAll = false;
11782         
11783         if(this.inputEl().dom.selectionEnd > 0){
11784             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11785         }
11786         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11787             event.preventDefault();
11788             this.setValue('');
11789             return;
11790         }
11791         
11792         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11793             
11794             event.preventDefault();
11795             // this is very hacky as keydown always get's upper case.
11796             //
11797             var cc = String.fromCharCode(event.getCharCode());
11798             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11799             
11800         }
11801     },
11802     adjustWidth : function(tag, w){
11803         tag = tag.toLowerCase();
11804         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11805             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11806                 if(tag == 'input'){
11807                     return w + 2;
11808                 }
11809                 if(tag == 'textarea'){
11810                     return w-2;
11811                 }
11812             }else if(Roo.isOpera){
11813                 if(tag == 'input'){
11814                     return w + 2;
11815                 }
11816                 if(tag == 'textarea'){
11817                     return w-2;
11818                 }
11819             }
11820         }
11821         return w;
11822     },
11823     
11824     setFieldLabel : function(v)
11825     {
11826         if(!this.rendered){
11827             return;
11828         }
11829         
11830         if(this.indicatorEl()){
11831             var ar = this.el.select('label > span',true);
11832             
11833             if (ar.elements.length) {
11834                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11835                 this.fieldLabel = v;
11836                 return;
11837             }
11838             
11839             var br = this.el.select('label',true);
11840             
11841             if(br.elements.length) {
11842                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11843                 this.fieldLabel = v;
11844                 return;
11845             }
11846             
11847             Roo.log('Cannot Found any of label > span || label in input');
11848             return;
11849         }
11850         
11851         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11852         this.fieldLabel = v;
11853         
11854         
11855     }
11856 });
11857
11858  
11859 /*
11860  * - LGPL
11861  *
11862  * Input
11863  * 
11864  */
11865
11866 /**
11867  * @class Roo.bootstrap.TextArea
11868  * @extends Roo.bootstrap.Input
11869  * Bootstrap TextArea class
11870  * @cfg {Number} cols Specifies the visible width of a text area
11871  * @cfg {Number} rows Specifies the visible number of lines in a text area
11872  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11873  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11874  * @cfg {string} html text
11875  * 
11876  * @constructor
11877  * Create a new TextArea
11878  * @param {Object} config The config object
11879  */
11880
11881 Roo.bootstrap.TextArea = function(config){
11882     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11883    
11884 };
11885
11886 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11887      
11888     cols : false,
11889     rows : 5,
11890     readOnly : false,
11891     warp : 'soft',
11892     resize : false,
11893     value: false,
11894     html: false,
11895     
11896     getAutoCreate : function(){
11897         
11898         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11899         
11900         var id = Roo.id();
11901         
11902         var cfg = {};
11903         
11904         if(this.inputType != 'hidden'){
11905             cfg.cls = 'form-group' //input-group
11906         }
11907         
11908         var input =  {
11909             tag: 'textarea',
11910             id : id,
11911             warp : this.warp,
11912             rows : this.rows,
11913             value : this.value || '',
11914             html: this.html || '',
11915             cls : 'form-control',
11916             placeholder : this.placeholder || '' 
11917             
11918         };
11919         
11920         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11921             input.maxLength = this.maxLength;
11922         }
11923         
11924         if(this.resize){
11925             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11926         }
11927         
11928         if(this.cols){
11929             input.cols = this.cols;
11930         }
11931         
11932         if (this.readOnly) {
11933             input.readonly = true;
11934         }
11935         
11936         if (this.name) {
11937             input.name = this.name;
11938         }
11939         
11940         if (this.size) {
11941             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11942         }
11943         
11944         var settings=this;
11945         ['xs','sm','md','lg'].map(function(size){
11946             if (settings[size]) {
11947                 cfg.cls += ' col-' + size + '-' + settings[size];
11948             }
11949         });
11950         
11951         var inputblock = input;
11952         
11953         if(this.hasFeedback && !this.allowBlank){
11954             
11955             var feedback = {
11956                 tag: 'span',
11957                 cls: 'glyphicon form-control-feedback'
11958             };
11959
11960             inputblock = {
11961                 cls : 'has-feedback',
11962                 cn :  [
11963                     input,
11964                     feedback
11965                 ] 
11966             };  
11967         }
11968         
11969         
11970         if (this.before || this.after) {
11971             
11972             inputblock = {
11973                 cls : 'input-group',
11974                 cn :  [] 
11975             };
11976             if (this.before) {
11977                 inputblock.cn.push({
11978                     tag :'span',
11979                     cls : 'input-group-addon',
11980                     html : this.before
11981                 });
11982             }
11983             
11984             inputblock.cn.push(input);
11985             
11986             if(this.hasFeedback && !this.allowBlank){
11987                 inputblock.cls += ' has-feedback';
11988                 inputblock.cn.push(feedback);
11989             }
11990             
11991             if (this.after) {
11992                 inputblock.cn.push({
11993                     tag :'span',
11994                     cls : 'input-group-addon',
11995                     html : this.after
11996                 });
11997             }
11998             
11999         }
12000         
12001         if (align ==='left' && this.fieldLabel.length) {
12002             cfg.cn = [
12003                 {
12004                     tag: 'label',
12005                     'for' :  id,
12006                     cls : 'control-label',
12007                     html : this.fieldLabel
12008                 },
12009                 {
12010                     cls : "",
12011                     cn: [
12012                         inputblock
12013                     ]
12014                 }
12015
12016             ];
12017             
12018             if(this.labelWidth > 12){
12019                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12020             }
12021
12022             if(this.labelWidth < 13 && this.labelmd == 0){
12023                 this.labelmd = this.labelWidth;
12024             }
12025
12026             if(this.labellg > 0){
12027                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12028                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12029             }
12030
12031             if(this.labelmd > 0){
12032                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12033                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12034             }
12035
12036             if(this.labelsm > 0){
12037                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12038                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12039             }
12040
12041             if(this.labelxs > 0){
12042                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12043                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12044             }
12045             
12046         } else if ( this.fieldLabel.length) {
12047             cfg.cn = [
12048
12049                {
12050                    tag: 'label',
12051                    //cls : 'input-group-addon',
12052                    html : this.fieldLabel
12053
12054                },
12055
12056                inputblock
12057
12058            ];
12059
12060         } else {
12061
12062             cfg.cn = [
12063
12064                 inputblock
12065
12066             ];
12067                 
12068         }
12069         
12070         if (this.disabled) {
12071             input.disabled=true;
12072         }
12073         
12074         return cfg;
12075         
12076     },
12077     /**
12078      * return the real textarea element.
12079      */
12080     inputEl: function ()
12081     {
12082         return this.el.select('textarea.form-control',true).first();
12083     },
12084     
12085     /**
12086      * Clear any invalid styles/messages for this field
12087      */
12088     clearInvalid : function()
12089     {
12090         
12091         if(!this.el || this.preventMark){ // not rendered
12092             return;
12093         }
12094         
12095         var label = this.el.select('label', true).first();
12096         var icon = this.el.select('i.fa-star', true).first();
12097         
12098         if(label && icon){
12099             icon.remove();
12100         }
12101         this.el.removeClass( this.validClass);
12102         this.inputEl().removeClass('is-invalid');
12103          
12104         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12105             
12106             var feedback = this.el.select('.form-control-feedback', true).first();
12107             
12108             if(feedback){
12109                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12110             }
12111             
12112         }
12113         
12114         this.fireEvent('valid', this);
12115     },
12116     
12117      /**
12118      * Mark this field as valid
12119      */
12120     markValid : function()
12121     {
12122         if(!this.el  || this.preventMark){ // not rendered
12123             return;
12124         }
12125         
12126         this.el.removeClass([this.invalidClass, this.validClass]);
12127         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12128         
12129         var feedback = this.el.select('.form-control-feedback', true).first();
12130             
12131         if(feedback){
12132             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12133         }
12134
12135         if(this.disabled || this.allowBlank){
12136             return;
12137         }
12138         
12139         var label = this.el.select('label', true).first();
12140         var icon = this.el.select('i.fa-star', true).first();
12141         
12142         if(label && icon){
12143             icon.remove();
12144         }
12145         if (Roo.bootstrap.version == 3) {
12146             this.el.addClass(this.validClass);
12147         } else {
12148             this.inputEl().addClass('is-valid');
12149         }
12150         
12151         
12152         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12153             
12154             var feedback = this.el.select('.form-control-feedback', true).first();
12155             
12156             if(feedback){
12157                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12158                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12159             }
12160             
12161         }
12162         
12163         this.fireEvent('valid', this);
12164     },
12165     
12166      /**
12167      * Mark this field as invalid
12168      * @param {String} msg The validation message
12169      */
12170     markInvalid : function(msg)
12171     {
12172         if(!this.el  || this.preventMark){ // not rendered
12173             return;
12174         }
12175         
12176         this.el.removeClass([this.invalidClass, this.validClass]);
12177         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12178         
12179         var feedback = this.el.select('.form-control-feedback', true).first();
12180             
12181         if(feedback){
12182             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12183         }
12184
12185         if(this.disabled || this.allowBlank){
12186             return;
12187         }
12188         
12189         var label = this.el.select('label', true).first();
12190         var icon = this.el.select('i.fa-star', true).first();
12191         
12192         if(!this.getValue().length && label && !icon){
12193             this.el.createChild({
12194                 tag : 'i',
12195                 cls : 'text-danger fa fa-lg fa-star',
12196                 tooltip : 'This field is required',
12197                 style : 'margin-right:5px;'
12198             }, label, true);
12199         }
12200         
12201         if (Roo.bootstrap.version == 3) {
12202             this.el.addClass(this.invalidClass);
12203         } else {
12204             this.inputEl().addClass('is-invalid');
12205         }
12206         
12207         // fixme ... this may be depricated need to test..
12208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12209             
12210             var feedback = this.el.select('.form-control-feedback', true).first();
12211             
12212             if(feedback){
12213                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12214                 
12215                 if(this.getValue().length || this.forceFeedback){
12216                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12217                 }
12218                 
12219             }
12220             
12221         }
12222         
12223         this.fireEvent('invalid', this, msg);
12224     }
12225 });
12226
12227  
12228 /*
12229  * - LGPL
12230  *
12231  * trigger field - base class for combo..
12232  * 
12233  */
12234  
12235 /**
12236  * @class Roo.bootstrap.TriggerField
12237  * @extends Roo.bootstrap.Input
12238  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12239  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12240  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12241  * for which you can provide a custom implementation.  For example:
12242  * <pre><code>
12243 var trigger = new Roo.bootstrap.TriggerField();
12244 trigger.onTriggerClick = myTriggerFn;
12245 trigger.applyTo('my-field');
12246 </code></pre>
12247  *
12248  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12249  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12250  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12251  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12252  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12253
12254  * @constructor
12255  * Create a new TriggerField.
12256  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12257  * to the base TextField)
12258  */
12259 Roo.bootstrap.TriggerField = function(config){
12260     this.mimicing = false;
12261     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12262 };
12263
12264 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12265     /**
12266      * @cfg {String} triggerClass A CSS class to apply to the trigger
12267      */
12268      /**
12269      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12270      */
12271     hideTrigger:false,
12272
12273     /**
12274      * @cfg {Boolean} removable (true|false) special filter default false
12275      */
12276     removable : false,
12277     
12278     /** @cfg {Boolean} grow @hide */
12279     /** @cfg {Number} growMin @hide */
12280     /** @cfg {Number} growMax @hide */
12281
12282     /**
12283      * @hide 
12284      * @method
12285      */
12286     autoSize: Roo.emptyFn,
12287     // private
12288     monitorTab : true,
12289     // private
12290     deferHeight : true,
12291
12292     
12293     actionMode : 'wrap',
12294     
12295     caret : false,
12296     
12297     
12298     getAutoCreate : function(){
12299        
12300         var align = this.labelAlign || this.parentLabelAlign();
12301         
12302         var id = Roo.id();
12303         
12304         var cfg = {
12305             cls: 'form-group' //input-group
12306         };
12307         
12308         
12309         var input =  {
12310             tag: 'input',
12311             id : id,
12312             type : this.inputType,
12313             cls : 'form-control',
12314             autocomplete: 'new-password',
12315             placeholder : this.placeholder || '' 
12316             
12317         };
12318         if (this.name) {
12319             input.name = this.name;
12320         }
12321         if (this.size) {
12322             input.cls += ' input-' + this.size;
12323         }
12324         
12325         if (this.disabled) {
12326             input.disabled=true;
12327         }
12328         
12329         var inputblock = input;
12330         
12331         if(this.hasFeedback && !this.allowBlank){
12332             
12333             var feedback = {
12334                 tag: 'span',
12335                 cls: 'glyphicon form-control-feedback'
12336             };
12337             
12338             if(this.removable && !this.editable  ){
12339                 inputblock = {
12340                     cls : 'has-feedback',
12341                     cn :  [
12342                         inputblock,
12343                         {
12344                             tag: 'button',
12345                             html : 'x',
12346                             cls : 'roo-combo-removable-btn close'
12347                         },
12348                         feedback
12349                     ] 
12350                 };
12351             } else {
12352                 inputblock = {
12353                     cls : 'has-feedback',
12354                     cn :  [
12355                         inputblock,
12356                         feedback
12357                     ] 
12358                 };
12359             }
12360
12361         } else {
12362             if(this.removable && !this.editable ){
12363                 inputblock = {
12364                     cls : 'roo-removable',
12365                     cn :  [
12366                         inputblock,
12367                         {
12368                             tag: 'button',
12369                             html : 'x',
12370                             cls : 'roo-combo-removable-btn close'
12371                         }
12372                     ] 
12373                 };
12374             }
12375         }
12376         
12377         if (this.before || this.after) {
12378             
12379             inputblock = {
12380                 cls : 'input-group',
12381                 cn :  [] 
12382             };
12383             if (this.before) {
12384                 inputblock.cn.push({
12385                     tag :'span',
12386                     cls : 'input-group-addon input-group-prepend input-group-text',
12387                     html : this.before
12388                 });
12389             }
12390             
12391             inputblock.cn.push(input);
12392             
12393             if(this.hasFeedback && !this.allowBlank){
12394                 inputblock.cls += ' has-feedback';
12395                 inputblock.cn.push(feedback);
12396             }
12397             
12398             if (this.after) {
12399                 inputblock.cn.push({
12400                     tag :'span',
12401                     cls : 'input-group-addon input-group-append input-group-text',
12402                     html : this.after
12403                 });
12404             }
12405             
12406         };
12407         
12408       
12409         
12410         var ibwrap = inputblock;
12411         
12412         if(this.multiple){
12413             ibwrap = {
12414                 tag: 'ul',
12415                 cls: 'roo-select2-choices',
12416                 cn:[
12417                     {
12418                         tag: 'li',
12419                         cls: 'roo-select2-search-field',
12420                         cn: [
12421
12422                             inputblock
12423                         ]
12424                     }
12425                 ]
12426             };
12427                 
12428         }
12429         
12430         var combobox = {
12431             cls: 'roo-select2-container input-group',
12432             cn: [
12433                  {
12434                     tag: 'input',
12435                     type : 'hidden',
12436                     cls: 'form-hidden-field'
12437                 },
12438                 ibwrap
12439             ]
12440         };
12441         
12442         if(!this.multiple && this.showToggleBtn){
12443             
12444             var caret = {
12445                         tag: 'span',
12446                         cls: 'caret'
12447              };
12448             if (this.caret != false) {
12449                 caret = {
12450                      tag: 'i',
12451                      cls: 'fa fa-' + this.caret
12452                 };
12453                 
12454             }
12455             
12456             combobox.cn.push({
12457                 tag :'span',
12458                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12459                 cn : [
12460                     Roo.bootstrap.version == 3 ? caret : '',
12461                     {
12462                         tag: 'span',
12463                         cls: 'combobox-clear',
12464                         cn  : [
12465                             {
12466                                 tag : 'i',
12467                                 cls: 'icon-remove'
12468                             }
12469                         ]
12470                     }
12471                 ]
12472
12473             })
12474         }
12475         
12476         if(this.multiple){
12477             combobox.cls += ' roo-select2-container-multi';
12478         }
12479          var indicator = {
12480             tag : 'i',
12481             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12482             tooltip : 'This field is required'
12483         };
12484         if (Roo.bootstrap.version == 4) {
12485             indicator = {
12486                 tag : 'i',
12487                 style : 'display:none'
12488             };
12489         }
12490         
12491         
12492         if (align ==='left' && this.fieldLabel.length) {
12493             
12494             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12495
12496             cfg.cn = [
12497                 indicator,
12498                 {
12499                     tag: 'label',
12500                     'for' :  id,
12501                     cls : 'control-label',
12502                     html : this.fieldLabel
12503
12504                 },
12505                 {
12506                     cls : "", 
12507                     cn: [
12508                         combobox
12509                     ]
12510                 }
12511
12512             ];
12513             
12514             var labelCfg = cfg.cn[1];
12515             var contentCfg = cfg.cn[2];
12516             
12517             if(this.indicatorpos == 'right'){
12518                 cfg.cn = [
12519                     {
12520                         tag: 'label',
12521                         'for' :  id,
12522                         cls : 'control-label',
12523                         cn : [
12524                             {
12525                                 tag : 'span',
12526                                 html : this.fieldLabel
12527                             },
12528                             indicator
12529                         ]
12530                     },
12531                     {
12532                         cls : "", 
12533                         cn: [
12534                             combobox
12535                         ]
12536                     }
12537
12538                 ];
12539                 
12540                 labelCfg = cfg.cn[0];
12541                 contentCfg = cfg.cn[1];
12542             }
12543             
12544             if(this.labelWidth > 12){
12545                 labelCfg.style = "width: " + this.labelWidth + 'px';
12546             }
12547             
12548             if(this.labelWidth < 13 && this.labelmd == 0){
12549                 this.labelmd = this.labelWidth;
12550             }
12551             
12552             if(this.labellg > 0){
12553                 labelCfg.cls += ' col-lg-' + this.labellg;
12554                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12555             }
12556             
12557             if(this.labelmd > 0){
12558                 labelCfg.cls += ' col-md-' + this.labelmd;
12559                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12560             }
12561             
12562             if(this.labelsm > 0){
12563                 labelCfg.cls += ' col-sm-' + this.labelsm;
12564                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12565             }
12566             
12567             if(this.labelxs > 0){
12568                 labelCfg.cls += ' col-xs-' + this.labelxs;
12569                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12570             }
12571             
12572         } else if ( this.fieldLabel.length) {
12573 //                Roo.log(" label");
12574             cfg.cn = [
12575                 indicator,
12576                {
12577                    tag: 'label',
12578                    //cls : 'input-group-addon',
12579                    html : this.fieldLabel
12580
12581                },
12582
12583                combobox
12584
12585             ];
12586             
12587             if(this.indicatorpos == 'right'){
12588                 
12589                 cfg.cn = [
12590                     {
12591                        tag: 'label',
12592                        cn : [
12593                            {
12594                                tag : 'span',
12595                                html : this.fieldLabel
12596                            },
12597                            indicator
12598                        ]
12599
12600                     },
12601                     combobox
12602
12603                 ];
12604
12605             }
12606
12607         } else {
12608             
12609 //                Roo.log(" no label && no align");
12610                 cfg = combobox
12611                      
12612                 
12613         }
12614         
12615         var settings=this;
12616         ['xs','sm','md','lg'].map(function(size){
12617             if (settings[size]) {
12618                 cfg.cls += ' col-' + size + '-' + settings[size];
12619             }
12620         });
12621         
12622         return cfg;
12623         
12624     },
12625     
12626     
12627     
12628     // private
12629     onResize : function(w, h){
12630 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12631 //        if(typeof w == 'number'){
12632 //            var x = w - this.trigger.getWidth();
12633 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12634 //            this.trigger.setStyle('left', x+'px');
12635 //        }
12636     },
12637
12638     // private
12639     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12640
12641     // private
12642     getResizeEl : function(){
12643         return this.inputEl();
12644     },
12645
12646     // private
12647     getPositionEl : function(){
12648         return this.inputEl();
12649     },
12650
12651     // private
12652     alignErrorIcon : function(){
12653         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12654     },
12655
12656     // private
12657     initEvents : function(){
12658         
12659         this.createList();
12660         
12661         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12662         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12663         if(!this.multiple && this.showToggleBtn){
12664             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12665             if(this.hideTrigger){
12666                 this.trigger.setDisplayed(false);
12667             }
12668             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12669         }
12670         
12671         if(this.multiple){
12672             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12673         }
12674         
12675         if(this.removable && !this.editable && !this.tickable){
12676             var close = this.closeTriggerEl();
12677             
12678             if(close){
12679                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12680                 close.on('click', this.removeBtnClick, this, close);
12681             }
12682         }
12683         
12684         //this.trigger.addClassOnOver('x-form-trigger-over');
12685         //this.trigger.addClassOnClick('x-form-trigger-click');
12686         
12687         //if(!this.width){
12688         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12689         //}
12690     },
12691     
12692     closeTriggerEl : function()
12693     {
12694         var close = this.el.select('.roo-combo-removable-btn', true).first();
12695         return close ? close : false;
12696     },
12697     
12698     removeBtnClick : function(e, h, el)
12699     {
12700         e.preventDefault();
12701         
12702         if(this.fireEvent("remove", this) !== false){
12703             this.reset();
12704             this.fireEvent("afterremove", this)
12705         }
12706     },
12707     
12708     createList : function()
12709     {
12710         this.list = Roo.get(document.body).createChild({
12711             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12712             cls: 'typeahead typeahead-long dropdown-menu shadow',
12713             style: 'display:none'
12714         });
12715         
12716         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12717         
12718     },
12719
12720     // private
12721     initTrigger : function(){
12722        
12723     },
12724
12725     // private
12726     onDestroy : function(){
12727         if(this.trigger){
12728             this.trigger.removeAllListeners();
12729           //  this.trigger.remove();
12730         }
12731         //if(this.wrap){
12732         //    this.wrap.remove();
12733         //}
12734         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12735     },
12736
12737     // private
12738     onFocus : function(){
12739         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12740         /*
12741         if(!this.mimicing){
12742             this.wrap.addClass('x-trigger-wrap-focus');
12743             this.mimicing = true;
12744             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12745             if(this.monitorTab){
12746                 this.el.on("keydown", this.checkTab, this);
12747             }
12748         }
12749         */
12750     },
12751
12752     // private
12753     checkTab : function(e){
12754         if(e.getKey() == e.TAB){
12755             this.triggerBlur();
12756         }
12757     },
12758
12759     // private
12760     onBlur : function(){
12761         // do nothing
12762     },
12763
12764     // private
12765     mimicBlur : function(e, t){
12766         /*
12767         if(!this.wrap.contains(t) && this.validateBlur()){
12768             this.triggerBlur();
12769         }
12770         */
12771     },
12772
12773     // private
12774     triggerBlur : function(){
12775         this.mimicing = false;
12776         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12777         if(this.monitorTab){
12778             this.el.un("keydown", this.checkTab, this);
12779         }
12780         //this.wrap.removeClass('x-trigger-wrap-focus');
12781         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12782     },
12783
12784     // private
12785     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12786     validateBlur : function(e, t){
12787         return true;
12788     },
12789
12790     // private
12791     onDisable : function(){
12792         this.inputEl().dom.disabled = true;
12793         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12794         //if(this.wrap){
12795         //    this.wrap.addClass('x-item-disabled');
12796         //}
12797     },
12798
12799     // private
12800     onEnable : function(){
12801         this.inputEl().dom.disabled = false;
12802         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12803         //if(this.wrap){
12804         //    this.el.removeClass('x-item-disabled');
12805         //}
12806     },
12807
12808     // private
12809     onShow : function(){
12810         var ae = this.getActionEl();
12811         
12812         if(ae){
12813             ae.dom.style.display = '';
12814             ae.dom.style.visibility = 'visible';
12815         }
12816     },
12817
12818     // private
12819     
12820     onHide : function(){
12821         var ae = this.getActionEl();
12822         ae.dom.style.display = 'none';
12823     },
12824
12825     /**
12826      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12827      * by an implementing function.
12828      * @method
12829      * @param {EventObject} e
12830      */
12831     onTriggerClick : Roo.emptyFn
12832 });
12833  
12834 /*
12835 * Licence: LGPL
12836 */
12837
12838 /**
12839  * @class Roo.bootstrap.CardUploader
12840  * @extends Roo.bootstrap.Button
12841  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12842  * @cfg {Number} errorTimeout default 3000
12843  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12844  * @cfg {Array}  html The button text.
12845
12846  *
12847  * @constructor
12848  * Create a new CardUploader
12849  * @param {Object} config The config object
12850  */
12851
12852 Roo.bootstrap.CardUploader = function(config){
12853     
12854  
12855     
12856     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12857     
12858     
12859     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12860         return r.data.id
12861      });
12862     
12863      this.addEvents({
12864          // raw events
12865         /**
12866          * @event preview
12867          * When a image is clicked on - and needs to display a slideshow or similar..
12868          * @param {Roo.bootstrap.Card} this
12869          * @param {Object} The image information data 
12870          *
12871          */
12872         'preview' : true,
12873          /**
12874          * @event download
12875          * When a the download link is clicked
12876          * @param {Roo.bootstrap.Card} this
12877          * @param {Object} The image information data  contains 
12878          */
12879         'download' : true
12880         
12881     });
12882 };
12883  
12884 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12885     
12886      
12887     errorTimeout : 3000,
12888      
12889     images : false,
12890    
12891     fileCollection : false,
12892     allowBlank : true,
12893     
12894     getAutoCreate : function()
12895     {
12896         
12897         var cfg =  {
12898             cls :'form-group' ,
12899             cn : [
12900                
12901                 {
12902                     tag: 'label',
12903                    //cls : 'input-group-addon',
12904                     html : this.fieldLabel
12905
12906                 },
12907
12908                 {
12909                     tag: 'input',
12910                     type : 'hidden',
12911                     name : this.name,
12912                     value : this.value,
12913                     cls : 'd-none  form-control'
12914                 },
12915                 
12916                 {
12917                     tag: 'input',
12918                     multiple : 'multiple',
12919                     type : 'file',
12920                     cls : 'd-none  roo-card-upload-selector'
12921                 },
12922                 
12923                 {
12924                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12925                 },
12926                 {
12927                     cls : 'card-columns roo-card-uploader-container'
12928                 }
12929
12930             ]
12931         };
12932            
12933          
12934         return cfg;
12935     },
12936     
12937     getChildContainer : function() /// what children are added to.
12938     {
12939         return this.containerEl;
12940     },
12941    
12942     getButtonContainer : function() /// what children are added to.
12943     {
12944         return this.el.select(".roo-card-uploader-button-container").first();
12945     },
12946    
12947     initEvents : function()
12948     {
12949         
12950         Roo.bootstrap.Input.prototype.initEvents.call(this);
12951         
12952         var t = this;
12953         this.addxtype({
12954             xns: Roo.bootstrap,
12955
12956             xtype : 'Button',
12957             container_method : 'getButtonContainer' ,            
12958             html :  this.html, // fix changable?
12959             cls : 'w-100 ',
12960             listeners : {
12961                 'click' : function(btn, e) {
12962                     t.onClick(e);
12963                 }
12964             }
12965         });
12966         
12967         
12968         
12969         
12970         this.urlAPI = (window.createObjectURL && window) || 
12971                                 (window.URL && URL.revokeObjectURL && URL) || 
12972                                 (window.webkitURL && webkitURL);
12973                         
12974          
12975          
12976          
12977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12978         
12979         this.selectorEl.on('change', this.onFileSelected, this);
12980         if (this.images) {
12981             var t = this;
12982             this.images.forEach(function(img) {
12983                 t.addCard(img)
12984             });
12985             this.images = false;
12986         }
12987         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12988          
12989        
12990     },
12991     
12992    
12993     onClick : function(e)
12994     {
12995         e.preventDefault();
12996          
12997         this.selectorEl.dom.click();
12998          
12999     },
13000     
13001     onFileSelected : function(e)
13002     {
13003         e.preventDefault();
13004         
13005         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13006             return;
13007         }
13008         
13009         Roo.each(this.selectorEl.dom.files, function(file){    
13010             this.addFile(file);
13011         }, this);
13012          
13013     },
13014     
13015       
13016     
13017       
13018     
13019     addFile : function(file)
13020     {
13021            
13022         if(typeof(file) === 'string'){
13023             throw "Add file by name?"; // should not happen
13024             return;
13025         }
13026         
13027         if(!file || !this.urlAPI){
13028             return;
13029         }
13030         
13031         // file;
13032         // file.type;
13033         
13034         var _this = this;
13035         
13036         
13037         var url = _this.urlAPI.createObjectURL( file);
13038            
13039         this.addCard({
13040             id : Roo.bootstrap.CardUploader.ID--,
13041             is_uploaded : false,
13042             src : url,
13043             srcfile : file,
13044             title : file.name,
13045             mimetype : file.type,
13046             preview : false,
13047             is_deleted : 0
13048         });
13049         
13050     },
13051     
13052     /**
13053      * addCard - add an Attachment to the uploader
13054      * @param data - the data about the image to upload
13055      *
13056      * {
13057           id : 123
13058           title : "Title of file",
13059           is_uploaded : false,
13060           src : "http://.....",
13061           srcfile : { the File upload object },
13062           mimetype : file.type,
13063           preview : false,
13064           is_deleted : 0
13065           .. any other data...
13066         }
13067      *
13068      * 
13069     */
13070     
13071     addCard : function (data)
13072     {
13073         // hidden input element?
13074         // if the file is not an image...
13075         //then we need to use something other that and header_image
13076         var t = this;
13077         //   remove.....
13078         var footer = [
13079             {
13080                 xns : Roo.bootstrap,
13081                 xtype : 'CardFooter',
13082                  items: [
13083                     {
13084                         xns : Roo.bootstrap,
13085                         xtype : 'Element',
13086                         cls : 'd-flex',
13087                         items : [
13088                             
13089                             {
13090                                 xns : Roo.bootstrap,
13091                                 xtype : 'Button',
13092                                 html : String.format("<small>{0}</small>", data.title),
13093                                 cls : 'col-10 text-left',
13094                                 size: 'sm',
13095                                 weight: 'link',
13096                                 fa : 'download',
13097                                 listeners : {
13098                                     click : function() {
13099                                      
13100                                         t.fireEvent( "download", t, data );
13101                                     }
13102                                 }
13103                             },
13104                           
13105                             {
13106                                 xns : Roo.bootstrap,
13107                                 xtype : 'Button',
13108                                 style: 'max-height: 28px; ',
13109                                 size : 'sm',
13110                                 weight: 'danger',
13111                                 cls : 'col-2',
13112                                 fa : 'times',
13113                                 listeners : {
13114                                     click : function() {
13115                                         t.removeCard(data.id)
13116                                     }
13117                                 }
13118                             }
13119                         ]
13120                     }
13121                     
13122                 ] 
13123             }
13124             
13125         ];
13126         
13127         var cn = this.addxtype(
13128             {
13129                  
13130                 xns : Roo.bootstrap,
13131                 xtype : 'Card',
13132                 closeable : true,
13133                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13134                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13135                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13136                 data : data,
13137                 html : false,
13138                  
13139                 items : footer,
13140                 initEvents : function() {
13141                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13142                     var card = this;
13143                     this.imgEl = this.el.select('.card-img-top').first();
13144                     if (this.imgEl) {
13145                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13146                         this.imgEl.set({ 'pointer' : 'cursor' });
13147                                   
13148                     }
13149                     this.getCardFooter().addClass('p-1');
13150                     
13151                   
13152                 }
13153                 
13154             }
13155         );
13156         // dont' really need ot update items.
13157         // this.items.push(cn);
13158         this.fileCollection.add(cn);
13159         
13160         if (!data.srcfile) {
13161             this.updateInput();
13162             return;
13163         }
13164             
13165         var _t = this;
13166         var reader = new FileReader();
13167         reader.addEventListener("load", function() {  
13168             data.srcdata =  reader.result;
13169             _t.updateInput();
13170         });
13171         reader.readAsDataURL(data.srcfile);
13172         
13173         
13174         
13175     },
13176     removeCard : function(id)
13177     {
13178         
13179         var card  = this.fileCollection.get(id);
13180         card.data.is_deleted = 1;
13181         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13182         //this.fileCollection.remove(card);
13183         //this.items = this.items.filter(function(e) { return e != card });
13184         // dont' really need ot update items.
13185         card.el.dom.parentNode.removeChild(card.el.dom);
13186         this.updateInput();
13187
13188         
13189     },
13190     reset: function()
13191     {
13192         this.fileCollection.each(function(card) {
13193             if (card.el.dom && card.el.dom.parentNode) {
13194                 card.el.dom.parentNode.removeChild(card.el.dom);
13195             }
13196         });
13197         this.fileCollection.clear();
13198         this.updateInput();
13199     },
13200     
13201     updateInput : function()
13202     {
13203          var data = [];
13204         this.fileCollection.each(function(e) {
13205             data.push(e.data);
13206             
13207         });
13208         this.inputEl().dom.value = JSON.stringify(data);
13209         
13210         
13211         
13212     }
13213     
13214     
13215 });
13216
13217
13218 Roo.bootstrap.CardUploader.ID = -1;/*
13219  * Based on:
13220  * Ext JS Library 1.1.1
13221  * Copyright(c) 2006-2007, Ext JS, LLC.
13222  *
13223  * Originally Released Under LGPL - original licence link has changed is not relivant.
13224  *
13225  * Fork - LGPL
13226  * <script type="text/javascript">
13227  */
13228
13229
13230 /**
13231  * @class Roo.data.SortTypes
13232  * @singleton
13233  * Defines the default sorting (casting?) comparison functions used when sorting data.
13234  */
13235 Roo.data.SortTypes = {
13236     /**
13237      * Default sort that does nothing
13238      * @param {Mixed} s The value being converted
13239      * @return {Mixed} The comparison value
13240      */
13241     none : function(s){
13242         return s;
13243     },
13244     
13245     /**
13246      * The regular expression used to strip tags
13247      * @type {RegExp}
13248      * @property
13249      */
13250     stripTagsRE : /<\/?[^>]+>/gi,
13251     
13252     /**
13253      * Strips all HTML tags to sort on text only
13254      * @param {Mixed} s The value being converted
13255      * @return {String} The comparison value
13256      */
13257     asText : function(s){
13258         return String(s).replace(this.stripTagsRE, "");
13259     },
13260     
13261     /**
13262      * Strips all HTML tags to sort on text only - Case insensitive
13263      * @param {Mixed} s The value being converted
13264      * @return {String} The comparison value
13265      */
13266     asUCText : function(s){
13267         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13268     },
13269     
13270     /**
13271      * Case insensitive string
13272      * @param {Mixed} s The value being converted
13273      * @return {String} The comparison value
13274      */
13275     asUCString : function(s) {
13276         return String(s).toUpperCase();
13277     },
13278     
13279     /**
13280      * Date sorting
13281      * @param {Mixed} s The value being converted
13282      * @return {Number} The comparison value
13283      */
13284     asDate : function(s) {
13285         if(!s){
13286             return 0;
13287         }
13288         if(s instanceof Date){
13289             return s.getTime();
13290         }
13291         return Date.parse(String(s));
13292     },
13293     
13294     /**
13295      * Float sorting
13296      * @param {Mixed} s The value being converted
13297      * @return {Float} The comparison value
13298      */
13299     asFloat : function(s) {
13300         var val = parseFloat(String(s).replace(/,/g, ""));
13301         if(isNaN(val)) {
13302             val = 0;
13303         }
13304         return val;
13305     },
13306     
13307     /**
13308      * Integer sorting
13309      * @param {Mixed} s The value being converted
13310      * @return {Number} The comparison value
13311      */
13312     asInt : function(s) {
13313         var val = parseInt(String(s).replace(/,/g, ""));
13314         if(isNaN(val)) {
13315             val = 0;
13316         }
13317         return val;
13318     }
13319 };/*
13320  * Based on:
13321  * Ext JS Library 1.1.1
13322  * Copyright(c) 2006-2007, Ext JS, LLC.
13323  *
13324  * Originally Released Under LGPL - original licence link has changed is not relivant.
13325  *
13326  * Fork - LGPL
13327  * <script type="text/javascript">
13328  */
13329
13330 /**
13331 * @class Roo.data.Record
13332  * Instances of this class encapsulate both record <em>definition</em> information, and record
13333  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13334  * to access Records cached in an {@link Roo.data.Store} object.<br>
13335  * <p>
13336  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13337  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13338  * objects.<br>
13339  * <p>
13340  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13341  * @constructor
13342  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13343  * {@link #create}. The parameters are the same.
13344  * @param {Array} data An associative Array of data values keyed by the field name.
13345  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13346  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13347  * not specified an integer id is generated.
13348  */
13349 Roo.data.Record = function(data, id){
13350     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13351     this.data = data;
13352 };
13353
13354 /**
13355  * Generate a constructor for a specific record layout.
13356  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13357  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13358  * Each field definition object may contain the following properties: <ul>
13359  * <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,
13360  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13361  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13362  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13363  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13364  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13365  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13366  * this may be omitted.</p></li>
13367  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13368  * <ul><li>auto (Default, implies no conversion)</li>
13369  * <li>string</li>
13370  * <li>int</li>
13371  * <li>float</li>
13372  * <li>boolean</li>
13373  * <li>date</li></ul></p></li>
13374  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13375  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13376  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13377  * by the Reader into an object that will be stored in the Record. It is passed the
13378  * following parameters:<ul>
13379  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13380  * </ul></p></li>
13381  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13382  * </ul>
13383  * <br>usage:<br><pre><code>
13384 var TopicRecord = Roo.data.Record.create(
13385     {name: 'title', mapping: 'topic_title'},
13386     {name: 'author', mapping: 'username'},
13387     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13388     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13389     {name: 'lastPoster', mapping: 'user2'},
13390     {name: 'excerpt', mapping: 'post_text'}
13391 );
13392
13393 var myNewRecord = new TopicRecord({
13394     title: 'Do my job please',
13395     author: 'noobie',
13396     totalPosts: 1,
13397     lastPost: new Date(),
13398     lastPoster: 'Animal',
13399     excerpt: 'No way dude!'
13400 });
13401 myStore.add(myNewRecord);
13402 </code></pre>
13403  * @method create
13404  * @static
13405  */
13406 Roo.data.Record.create = function(o){
13407     var f = function(){
13408         f.superclass.constructor.apply(this, arguments);
13409     };
13410     Roo.extend(f, Roo.data.Record);
13411     var p = f.prototype;
13412     p.fields = new Roo.util.MixedCollection(false, function(field){
13413         return field.name;
13414     });
13415     for(var i = 0, len = o.length; i < len; i++){
13416         p.fields.add(new Roo.data.Field(o[i]));
13417     }
13418     f.getField = function(name){
13419         return p.fields.get(name);  
13420     };
13421     return f;
13422 };
13423
13424 Roo.data.Record.AUTO_ID = 1000;
13425 Roo.data.Record.EDIT = 'edit';
13426 Roo.data.Record.REJECT = 'reject';
13427 Roo.data.Record.COMMIT = 'commit';
13428
13429 Roo.data.Record.prototype = {
13430     /**
13431      * Readonly flag - true if this record has been modified.
13432      * @type Boolean
13433      */
13434     dirty : false,
13435     editing : false,
13436     error: null,
13437     modified: null,
13438
13439     // private
13440     join : function(store){
13441         this.store = store;
13442     },
13443
13444     /**
13445      * Set the named field to the specified value.
13446      * @param {String} name The name of the field to set.
13447      * @param {Object} value The value to set the field to.
13448      */
13449     set : function(name, value){
13450         if(this.data[name] == value){
13451             return;
13452         }
13453         this.dirty = true;
13454         if(!this.modified){
13455             this.modified = {};
13456         }
13457         if(typeof this.modified[name] == 'undefined'){
13458             this.modified[name] = this.data[name];
13459         }
13460         this.data[name] = value;
13461         if(!this.editing && this.store){
13462             this.store.afterEdit(this);
13463         }       
13464     },
13465
13466     /**
13467      * Get the value of the named field.
13468      * @param {String} name The name of the field to get the value of.
13469      * @return {Object} The value of the field.
13470      */
13471     get : function(name){
13472         return this.data[name]; 
13473     },
13474
13475     // private
13476     beginEdit : function(){
13477         this.editing = true;
13478         this.modified = {}; 
13479     },
13480
13481     // private
13482     cancelEdit : function(){
13483         this.editing = false;
13484         delete this.modified;
13485     },
13486
13487     // private
13488     endEdit : function(){
13489         this.editing = false;
13490         if(this.dirty && this.store){
13491             this.store.afterEdit(this);
13492         }
13493     },
13494
13495     /**
13496      * Usually called by the {@link Roo.data.Store} which owns the Record.
13497      * Rejects all changes made to the Record since either creation, or the last commit operation.
13498      * Modified fields are reverted to their original values.
13499      * <p>
13500      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13501      * of reject operations.
13502      */
13503     reject : function(){
13504         var m = this.modified;
13505         for(var n in m){
13506             if(typeof m[n] != "function"){
13507                 this.data[n] = m[n];
13508             }
13509         }
13510         this.dirty = false;
13511         delete this.modified;
13512         this.editing = false;
13513         if(this.store){
13514             this.store.afterReject(this);
13515         }
13516     },
13517
13518     /**
13519      * Usually called by the {@link Roo.data.Store} which owns the Record.
13520      * Commits all changes made to the Record since either creation, or the last commit operation.
13521      * <p>
13522      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13523      * of commit operations.
13524      */
13525     commit : function(){
13526         this.dirty = false;
13527         delete this.modified;
13528         this.editing = false;
13529         if(this.store){
13530             this.store.afterCommit(this);
13531         }
13532     },
13533
13534     // private
13535     hasError : function(){
13536         return this.error != null;
13537     },
13538
13539     // private
13540     clearError : function(){
13541         this.error = null;
13542     },
13543
13544     /**
13545      * Creates a copy of this record.
13546      * @param {String} id (optional) A new record id if you don't want to use this record's id
13547      * @return {Record}
13548      */
13549     copy : function(newId) {
13550         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13551     }
13552 };/*
13553  * Based on:
13554  * Ext JS Library 1.1.1
13555  * Copyright(c) 2006-2007, Ext JS, LLC.
13556  *
13557  * Originally Released Under LGPL - original licence link has changed is not relivant.
13558  *
13559  * Fork - LGPL
13560  * <script type="text/javascript">
13561  */
13562
13563
13564
13565 /**
13566  * @class Roo.data.Store
13567  * @extends Roo.util.Observable
13568  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13569  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13570  * <p>
13571  * 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
13572  * has no knowledge of the format of the data returned by the Proxy.<br>
13573  * <p>
13574  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13575  * instances from the data object. These records are cached and made available through accessor functions.
13576  * @constructor
13577  * Creates a new Store.
13578  * @param {Object} config A config object containing the objects needed for the Store to access data,
13579  * and read the data into Records.
13580  */
13581 Roo.data.Store = function(config){
13582     this.data = new Roo.util.MixedCollection(false);
13583     this.data.getKey = function(o){
13584         return o.id;
13585     };
13586     this.baseParams = {};
13587     // private
13588     this.paramNames = {
13589         "start" : "start",
13590         "limit" : "limit",
13591         "sort" : "sort",
13592         "dir" : "dir",
13593         "multisort" : "_multisort"
13594     };
13595
13596     if(config && config.data){
13597         this.inlineData = config.data;
13598         delete config.data;
13599     }
13600
13601     Roo.apply(this, config);
13602     
13603     if(this.reader){ // reader passed
13604         this.reader = Roo.factory(this.reader, Roo.data);
13605         this.reader.xmodule = this.xmodule || false;
13606         if(!this.recordType){
13607             this.recordType = this.reader.recordType;
13608         }
13609         if(this.reader.onMetaChange){
13610             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13611         }
13612     }
13613
13614     if(this.recordType){
13615         this.fields = this.recordType.prototype.fields;
13616     }
13617     this.modified = [];
13618
13619     this.addEvents({
13620         /**
13621          * @event datachanged
13622          * Fires when the data cache has changed, and a widget which is using this Store
13623          * as a Record cache should refresh its view.
13624          * @param {Store} this
13625          */
13626         datachanged : true,
13627         /**
13628          * @event metachange
13629          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13630          * @param {Store} this
13631          * @param {Object} meta The JSON metadata
13632          */
13633         metachange : true,
13634         /**
13635          * @event add
13636          * Fires when Records have been added to the Store
13637          * @param {Store} this
13638          * @param {Roo.data.Record[]} records The array of Records added
13639          * @param {Number} index The index at which the record(s) were added
13640          */
13641         add : true,
13642         /**
13643          * @event remove
13644          * Fires when a Record has been removed from the Store
13645          * @param {Store} this
13646          * @param {Roo.data.Record} record The Record that was removed
13647          * @param {Number} index The index at which the record was removed
13648          */
13649         remove : true,
13650         /**
13651          * @event update
13652          * Fires when a Record has been updated
13653          * @param {Store} this
13654          * @param {Roo.data.Record} record The Record that was updated
13655          * @param {String} operation The update operation being performed.  Value may be one of:
13656          * <pre><code>
13657  Roo.data.Record.EDIT
13658  Roo.data.Record.REJECT
13659  Roo.data.Record.COMMIT
13660          * </code></pre>
13661          */
13662         update : true,
13663         /**
13664          * @event clear
13665          * Fires when the data cache has been cleared.
13666          * @param {Store} this
13667          */
13668         clear : true,
13669         /**
13670          * @event beforeload
13671          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13672          * the load action will be canceled.
13673          * @param {Store} this
13674          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13675          */
13676         beforeload : true,
13677         /**
13678          * @event beforeloadadd
13679          * Fires after a new set of Records has been loaded.
13680          * @param {Store} this
13681          * @param {Roo.data.Record[]} records The Records that were loaded
13682          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13683          */
13684         beforeloadadd : true,
13685         /**
13686          * @event load
13687          * Fires after a new set of Records has been loaded, before they are added to the store.
13688          * @param {Store} this
13689          * @param {Roo.data.Record[]} records The Records that were loaded
13690          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13691          * @params {Object} return from reader
13692          */
13693         load : true,
13694         /**
13695          * @event loadexception
13696          * Fires if an exception occurs in the Proxy during loading.
13697          * Called with the signature of the Proxy's "loadexception" event.
13698          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13699          * 
13700          * @param {Proxy} 
13701          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13702          * @param {Object} load options 
13703          * @param {Object} jsonData from your request (normally this contains the Exception)
13704          */
13705         loadexception : true
13706     });
13707     
13708     if(this.proxy){
13709         this.proxy = Roo.factory(this.proxy, Roo.data);
13710         this.proxy.xmodule = this.xmodule || false;
13711         this.relayEvents(this.proxy,  ["loadexception"]);
13712     }
13713     this.sortToggle = {};
13714     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13715
13716     Roo.data.Store.superclass.constructor.call(this);
13717
13718     if(this.inlineData){
13719         this.loadData(this.inlineData);
13720         delete this.inlineData;
13721     }
13722 };
13723
13724 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13725      /**
13726     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13727     * without a remote query - used by combo/forms at present.
13728     */
13729     
13730     /**
13731     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13732     */
13733     /**
13734     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13735     */
13736     /**
13737     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13738     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13739     */
13740     /**
13741     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13742     * on any HTTP request
13743     */
13744     /**
13745     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13746     */
13747     /**
13748     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13749     */
13750     multiSort: false,
13751     /**
13752     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13753     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13754     */
13755     remoteSort : false,
13756
13757     /**
13758     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13759      * loaded or when a record is removed. (defaults to false).
13760     */
13761     pruneModifiedRecords : false,
13762
13763     // private
13764     lastOptions : null,
13765
13766     /**
13767      * Add Records to the Store and fires the add event.
13768      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13769      */
13770     add : function(records){
13771         records = [].concat(records);
13772         for(var i = 0, len = records.length; i < len; i++){
13773             records[i].join(this);
13774         }
13775         var index = this.data.length;
13776         this.data.addAll(records);
13777         this.fireEvent("add", this, records, index);
13778     },
13779
13780     /**
13781      * Remove a Record from the Store and fires the remove event.
13782      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13783      */
13784     remove : function(record){
13785         var index = this.data.indexOf(record);
13786         this.data.removeAt(index);
13787  
13788         if(this.pruneModifiedRecords){
13789             this.modified.remove(record);
13790         }
13791         this.fireEvent("remove", this, record, index);
13792     },
13793
13794     /**
13795      * Remove all Records from the Store and fires the clear event.
13796      */
13797     removeAll : function(){
13798         this.data.clear();
13799         if(this.pruneModifiedRecords){
13800             this.modified = [];
13801         }
13802         this.fireEvent("clear", this);
13803     },
13804
13805     /**
13806      * Inserts Records to the Store at the given index and fires the add event.
13807      * @param {Number} index The start index at which to insert the passed Records.
13808      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13809      */
13810     insert : function(index, records){
13811         records = [].concat(records);
13812         for(var i = 0, len = records.length; i < len; i++){
13813             this.data.insert(index, records[i]);
13814             records[i].join(this);
13815         }
13816         this.fireEvent("add", this, records, index);
13817     },
13818
13819     /**
13820      * Get the index within the cache of the passed Record.
13821      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13822      * @return {Number} The index of the passed Record. Returns -1 if not found.
13823      */
13824     indexOf : function(record){
13825         return this.data.indexOf(record);
13826     },
13827
13828     /**
13829      * Get the index within the cache of the Record with the passed id.
13830      * @param {String} id The id of the Record to find.
13831      * @return {Number} The index of the Record. Returns -1 if not found.
13832      */
13833     indexOfId : function(id){
13834         return this.data.indexOfKey(id);
13835     },
13836
13837     /**
13838      * Get the Record with the specified id.
13839      * @param {String} id The id of the Record to find.
13840      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13841      */
13842     getById : function(id){
13843         return this.data.key(id);
13844     },
13845
13846     /**
13847      * Get the Record at the specified index.
13848      * @param {Number} index The index of the Record to find.
13849      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13850      */
13851     getAt : function(index){
13852         return this.data.itemAt(index);
13853     },
13854
13855     /**
13856      * Returns a range of Records between specified indices.
13857      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13858      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13859      * @return {Roo.data.Record[]} An array of Records
13860      */
13861     getRange : function(start, end){
13862         return this.data.getRange(start, end);
13863     },
13864
13865     // private
13866     storeOptions : function(o){
13867         o = Roo.apply({}, o);
13868         delete o.callback;
13869         delete o.scope;
13870         this.lastOptions = o;
13871     },
13872
13873     /**
13874      * Loads the Record cache from the configured Proxy using the configured Reader.
13875      * <p>
13876      * If using remote paging, then the first load call must specify the <em>start</em>
13877      * and <em>limit</em> properties in the options.params property to establish the initial
13878      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13879      * <p>
13880      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13881      * and this call will return before the new data has been loaded. Perform any post-processing
13882      * in a callback function, or in a "load" event handler.</strong>
13883      * <p>
13884      * @param {Object} options An object containing properties which control loading options:<ul>
13885      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13886      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13887      * passed the following arguments:<ul>
13888      * <li>r : Roo.data.Record[]</li>
13889      * <li>options: Options object from the load call</li>
13890      * <li>success: Boolean success indicator</li></ul></li>
13891      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13892      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13893      * </ul>
13894      */
13895     load : function(options){
13896         options = options || {};
13897         if(this.fireEvent("beforeload", this, options) !== false){
13898             this.storeOptions(options);
13899             var p = Roo.apply(options.params || {}, this.baseParams);
13900             // if meta was not loaded from remote source.. try requesting it.
13901             if (!this.reader.metaFromRemote) {
13902                 p._requestMeta = 1;
13903             }
13904             if(this.sortInfo && this.remoteSort){
13905                 var pn = this.paramNames;
13906                 p[pn["sort"]] = this.sortInfo.field;
13907                 p[pn["dir"]] = this.sortInfo.direction;
13908             }
13909             if (this.multiSort) {
13910                 var pn = this.paramNames;
13911                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13912             }
13913             
13914             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13915         }
13916     },
13917
13918     /**
13919      * Reloads the Record cache from the configured Proxy using the configured Reader and
13920      * the options from the last load operation performed.
13921      * @param {Object} options (optional) An object containing properties which may override the options
13922      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13923      * the most recently used options are reused).
13924      */
13925     reload : function(options){
13926         this.load(Roo.applyIf(options||{}, this.lastOptions));
13927     },
13928
13929     // private
13930     // Called as a callback by the Reader during a load operation.
13931     loadRecords : function(o, options, success){
13932         if(!o || success === false){
13933             if(success !== false){
13934                 this.fireEvent("load", this, [], options, o);
13935             }
13936             if(options.callback){
13937                 options.callback.call(options.scope || this, [], options, false);
13938             }
13939             return;
13940         }
13941         // if data returned failure - throw an exception.
13942         if (o.success === false) {
13943             // show a message if no listener is registered.
13944             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13945                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13946             }
13947             // loadmask wil be hooked into this..
13948             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13949             return;
13950         }
13951         var r = o.records, t = o.totalRecords || r.length;
13952         
13953         this.fireEvent("beforeloadadd", this, r, options, o);
13954         
13955         if(!options || options.add !== true){
13956             if(this.pruneModifiedRecords){
13957                 this.modified = [];
13958             }
13959             for(var i = 0, len = r.length; i < len; i++){
13960                 r[i].join(this);
13961             }
13962             if(this.snapshot){
13963                 this.data = this.snapshot;
13964                 delete this.snapshot;
13965             }
13966             this.data.clear();
13967             this.data.addAll(r);
13968             this.totalLength = t;
13969             this.applySort();
13970             this.fireEvent("datachanged", this);
13971         }else{
13972             this.totalLength = Math.max(t, this.data.length+r.length);
13973             this.add(r);
13974         }
13975         
13976         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13977                 
13978             var e = new Roo.data.Record({});
13979
13980             e.set(this.parent.displayField, this.parent.emptyTitle);
13981             e.set(this.parent.valueField, '');
13982
13983             this.insert(0, e);
13984         }
13985             
13986         this.fireEvent("load", this, r, options, o);
13987         if(options.callback){
13988             options.callback.call(options.scope || this, r, options, true);
13989         }
13990     },
13991
13992
13993     /**
13994      * Loads data from a passed data block. A Reader which understands the format of the data
13995      * must have been configured in the constructor.
13996      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13997      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13998      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13999      */
14000     loadData : function(o, append){
14001         var r = this.reader.readRecords(o);
14002         this.loadRecords(r, {add: append}, true);
14003     },
14004     
14005      /**
14006      * using 'cn' the nested child reader read the child array into it's child stores.
14007      * @param {Object} rec The record with a 'children array
14008      */
14009     loadDataFromChildren : function(rec)
14010     {
14011         this.loadData(this.reader.toLoadData(rec));
14012     },
14013     
14014
14015     /**
14016      * Gets the number of cached records.
14017      * <p>
14018      * <em>If using paging, this may not be the total size of the dataset. If the data object
14019      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14020      * the data set size</em>
14021      */
14022     getCount : function(){
14023         return this.data.length || 0;
14024     },
14025
14026     /**
14027      * Gets the total number of records in the dataset as returned by the server.
14028      * <p>
14029      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14030      * the dataset size</em>
14031      */
14032     getTotalCount : function(){
14033         return this.totalLength || 0;
14034     },
14035
14036     /**
14037      * Returns the sort state of the Store as an object with two properties:
14038      * <pre><code>
14039  field {String} The name of the field by which the Records are sorted
14040  direction {String} The sort order, "ASC" or "DESC"
14041      * </code></pre>
14042      */
14043     getSortState : function(){
14044         return this.sortInfo;
14045     },
14046
14047     // private
14048     applySort : function(){
14049         if(this.sortInfo && !this.remoteSort){
14050             var s = this.sortInfo, f = s.field;
14051             var st = this.fields.get(f).sortType;
14052             var fn = function(r1, r2){
14053                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14054                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14055             };
14056             this.data.sort(s.direction, fn);
14057             if(this.snapshot && this.snapshot != this.data){
14058                 this.snapshot.sort(s.direction, fn);
14059             }
14060         }
14061     },
14062
14063     /**
14064      * Sets the default sort column and order to be used by the next load operation.
14065      * @param {String} fieldName The name of the field to sort by.
14066      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14067      */
14068     setDefaultSort : function(field, dir){
14069         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14070     },
14071
14072     /**
14073      * Sort the Records.
14074      * If remote sorting is used, the sort is performed on the server, and the cache is
14075      * reloaded. If local sorting is used, the cache is sorted internally.
14076      * @param {String} fieldName The name of the field to sort by.
14077      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14078      */
14079     sort : function(fieldName, dir){
14080         var f = this.fields.get(fieldName);
14081         if(!dir){
14082             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14083             
14084             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14085                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14086             }else{
14087                 dir = f.sortDir;
14088             }
14089         }
14090         this.sortToggle[f.name] = dir;
14091         this.sortInfo = {field: f.name, direction: dir};
14092         if(!this.remoteSort){
14093             this.applySort();
14094             this.fireEvent("datachanged", this);
14095         }else{
14096             this.load(this.lastOptions);
14097         }
14098     },
14099
14100     /**
14101      * Calls the specified function for each of the Records in the cache.
14102      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14103      * Returning <em>false</em> aborts and exits the iteration.
14104      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14105      */
14106     each : function(fn, scope){
14107         this.data.each(fn, scope);
14108     },
14109
14110     /**
14111      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14112      * (e.g., during paging).
14113      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14114      */
14115     getModifiedRecords : function(){
14116         return this.modified;
14117     },
14118
14119     // private
14120     createFilterFn : function(property, value, anyMatch){
14121         if(!value.exec){ // not a regex
14122             value = String(value);
14123             if(value.length == 0){
14124                 return false;
14125             }
14126             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14127         }
14128         return function(r){
14129             return value.test(r.data[property]);
14130         };
14131     },
14132
14133     /**
14134      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14135      * @param {String} property A field on your records
14136      * @param {Number} start The record index to start at (defaults to 0)
14137      * @param {Number} end The last record index to include (defaults to length - 1)
14138      * @return {Number} The sum
14139      */
14140     sum : function(property, start, end){
14141         var rs = this.data.items, v = 0;
14142         start = start || 0;
14143         end = (end || end === 0) ? end : rs.length-1;
14144
14145         for(var i = start; i <= end; i++){
14146             v += (rs[i].data[property] || 0);
14147         }
14148         return v;
14149     },
14150
14151     /**
14152      * Filter the records by a specified property.
14153      * @param {String} field A field on your records
14154      * @param {String/RegExp} value Either a string that the field
14155      * should start with or a RegExp to test against the field
14156      * @param {Boolean} anyMatch True to match any part not just the beginning
14157      */
14158     filter : function(property, value, anyMatch){
14159         var fn = this.createFilterFn(property, value, anyMatch);
14160         return fn ? this.filterBy(fn) : this.clearFilter();
14161     },
14162
14163     /**
14164      * Filter by a function. The specified function will be called with each
14165      * record in this data source. If the function returns true the record is included,
14166      * otherwise it is filtered.
14167      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14168      * @param {Object} scope (optional) The scope of the function (defaults to this)
14169      */
14170     filterBy : function(fn, scope){
14171         this.snapshot = this.snapshot || this.data;
14172         this.data = this.queryBy(fn, scope||this);
14173         this.fireEvent("datachanged", this);
14174     },
14175
14176     /**
14177      * Query the records by a specified property.
14178      * @param {String} field A field on your records
14179      * @param {String/RegExp} value Either a string that the field
14180      * should start with or a RegExp to test against the field
14181      * @param {Boolean} anyMatch True to match any part not just the beginning
14182      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14183      */
14184     query : function(property, value, anyMatch){
14185         var fn = this.createFilterFn(property, value, anyMatch);
14186         return fn ? this.queryBy(fn) : this.data.clone();
14187     },
14188
14189     /**
14190      * Query by a function. The specified function will be called with each
14191      * record in this data source. If the function returns true the record is included
14192      * in the results.
14193      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14194      * @param {Object} scope (optional) The scope of the function (defaults to this)
14195       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14196      **/
14197     queryBy : function(fn, scope){
14198         var data = this.snapshot || this.data;
14199         return data.filterBy(fn, scope||this);
14200     },
14201
14202     /**
14203      * Collects unique values for a particular dataIndex from this store.
14204      * @param {String} dataIndex The property to collect
14205      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14206      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14207      * @return {Array} An array of the unique values
14208      **/
14209     collect : function(dataIndex, allowNull, bypassFilter){
14210         var d = (bypassFilter === true && this.snapshot) ?
14211                 this.snapshot.items : this.data.items;
14212         var v, sv, r = [], l = {};
14213         for(var i = 0, len = d.length; i < len; i++){
14214             v = d[i].data[dataIndex];
14215             sv = String(v);
14216             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14217                 l[sv] = true;
14218                 r[r.length] = v;
14219             }
14220         }
14221         return r;
14222     },
14223
14224     /**
14225      * Revert to a view of the Record cache with no filtering applied.
14226      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14227      */
14228     clearFilter : function(suppressEvent){
14229         if(this.snapshot && this.snapshot != this.data){
14230             this.data = this.snapshot;
14231             delete this.snapshot;
14232             if(suppressEvent !== true){
14233                 this.fireEvent("datachanged", this);
14234             }
14235         }
14236     },
14237
14238     // private
14239     afterEdit : function(record){
14240         if(this.modified.indexOf(record) == -1){
14241             this.modified.push(record);
14242         }
14243         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14244     },
14245     
14246     // private
14247     afterReject : function(record){
14248         this.modified.remove(record);
14249         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14250     },
14251
14252     // private
14253     afterCommit : function(record){
14254         this.modified.remove(record);
14255         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14256     },
14257
14258     /**
14259      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14260      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14261      */
14262     commitChanges : function(){
14263         var m = this.modified.slice(0);
14264         this.modified = [];
14265         for(var i = 0, len = m.length; i < len; i++){
14266             m[i].commit();
14267         }
14268     },
14269
14270     /**
14271      * Cancel outstanding changes on all changed records.
14272      */
14273     rejectChanges : function(){
14274         var m = this.modified.slice(0);
14275         this.modified = [];
14276         for(var i = 0, len = m.length; i < len; i++){
14277             m[i].reject();
14278         }
14279     },
14280
14281     onMetaChange : function(meta, rtype, o){
14282         this.recordType = rtype;
14283         this.fields = rtype.prototype.fields;
14284         delete this.snapshot;
14285         this.sortInfo = meta.sortInfo || this.sortInfo;
14286         this.modified = [];
14287         this.fireEvent('metachange', this, this.reader.meta);
14288     },
14289     
14290     moveIndex : function(data, type)
14291     {
14292         var index = this.indexOf(data);
14293         
14294         var newIndex = index + type;
14295         
14296         this.remove(data);
14297         
14298         this.insert(newIndex, data);
14299         
14300     }
14301 });/*
14302  * Based on:
14303  * Ext JS Library 1.1.1
14304  * Copyright(c) 2006-2007, Ext JS, LLC.
14305  *
14306  * Originally Released Under LGPL - original licence link has changed is not relivant.
14307  *
14308  * Fork - LGPL
14309  * <script type="text/javascript">
14310  */
14311
14312 /**
14313  * @class Roo.data.SimpleStore
14314  * @extends Roo.data.Store
14315  * Small helper class to make creating Stores from Array data easier.
14316  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14317  * @cfg {Array} fields An array of field definition objects, or field name strings.
14318  * @cfg {Object} an existing reader (eg. copied from another store)
14319  * @cfg {Array} data The multi-dimensional array of data
14320  * @constructor
14321  * @param {Object} config
14322  */
14323 Roo.data.SimpleStore = function(config)
14324 {
14325     Roo.data.SimpleStore.superclass.constructor.call(this, {
14326         isLocal : true,
14327         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14328                 id: config.id
14329             },
14330             Roo.data.Record.create(config.fields)
14331         ),
14332         proxy : new Roo.data.MemoryProxy(config.data)
14333     });
14334     this.load();
14335 };
14336 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14337  * Based on:
14338  * Ext JS Library 1.1.1
14339  * Copyright(c) 2006-2007, Ext JS, LLC.
14340  *
14341  * Originally Released Under LGPL - original licence link has changed is not relivant.
14342  *
14343  * Fork - LGPL
14344  * <script type="text/javascript">
14345  */
14346
14347 /**
14348 /**
14349  * @extends Roo.data.Store
14350  * @class Roo.data.JsonStore
14351  * Small helper class to make creating Stores for JSON data easier. <br/>
14352 <pre><code>
14353 var store = new Roo.data.JsonStore({
14354     url: 'get-images.php',
14355     root: 'images',
14356     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14357 });
14358 </code></pre>
14359  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14360  * JsonReader and HttpProxy (unless inline data is provided).</b>
14361  * @cfg {Array} fields An array of field definition objects, or field name strings.
14362  * @constructor
14363  * @param {Object} config
14364  */
14365 Roo.data.JsonStore = function(c){
14366     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14367         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14368         reader: new Roo.data.JsonReader(c, c.fields)
14369     }));
14370 };
14371 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14372  * Based on:
14373  * Ext JS Library 1.1.1
14374  * Copyright(c) 2006-2007, Ext JS, LLC.
14375  *
14376  * Originally Released Under LGPL - original licence link has changed is not relivant.
14377  *
14378  * Fork - LGPL
14379  * <script type="text/javascript">
14380  */
14381
14382  
14383 Roo.data.Field = function(config){
14384     if(typeof config == "string"){
14385         config = {name: config};
14386     }
14387     Roo.apply(this, config);
14388     
14389     if(!this.type){
14390         this.type = "auto";
14391     }
14392     
14393     var st = Roo.data.SortTypes;
14394     // named sortTypes are supported, here we look them up
14395     if(typeof this.sortType == "string"){
14396         this.sortType = st[this.sortType];
14397     }
14398     
14399     // set default sortType for strings and dates
14400     if(!this.sortType){
14401         switch(this.type){
14402             case "string":
14403                 this.sortType = st.asUCString;
14404                 break;
14405             case "date":
14406                 this.sortType = st.asDate;
14407                 break;
14408             default:
14409                 this.sortType = st.none;
14410         }
14411     }
14412
14413     // define once
14414     var stripRe = /[\$,%]/g;
14415
14416     // prebuilt conversion function for this field, instead of
14417     // switching every time we're reading a value
14418     if(!this.convert){
14419         var cv, dateFormat = this.dateFormat;
14420         switch(this.type){
14421             case "":
14422             case "auto":
14423             case undefined:
14424                 cv = function(v){ return v; };
14425                 break;
14426             case "string":
14427                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14428                 break;
14429             case "int":
14430                 cv = function(v){
14431                     return v !== undefined && v !== null && v !== '' ?
14432                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14433                     };
14434                 break;
14435             case "float":
14436                 cv = function(v){
14437                     return v !== undefined && v !== null && v !== '' ?
14438                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14439                     };
14440                 break;
14441             case "bool":
14442             case "boolean":
14443                 cv = function(v){ return v === true || v === "true" || v == 1; };
14444                 break;
14445             case "date":
14446                 cv = function(v){
14447                     if(!v){
14448                         return '';
14449                     }
14450                     if(v instanceof Date){
14451                         return v;
14452                     }
14453                     if(dateFormat){
14454                         if(dateFormat == "timestamp"){
14455                             return new Date(v*1000);
14456                         }
14457                         return Date.parseDate(v, dateFormat);
14458                     }
14459                     var parsed = Date.parse(v);
14460                     return parsed ? new Date(parsed) : null;
14461                 };
14462              break;
14463             
14464         }
14465         this.convert = cv;
14466     }
14467 };
14468
14469 Roo.data.Field.prototype = {
14470     dateFormat: null,
14471     defaultValue: "",
14472     mapping: null,
14473     sortType : null,
14474     sortDir : "ASC"
14475 };/*
14476  * Based on:
14477  * Ext JS Library 1.1.1
14478  * Copyright(c) 2006-2007, Ext JS, LLC.
14479  *
14480  * Originally Released Under LGPL - original licence link has changed is not relivant.
14481  *
14482  * Fork - LGPL
14483  * <script type="text/javascript">
14484  */
14485  
14486 // Base class for reading structured data from a data source.  This class is intended to be
14487 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14488
14489 /**
14490  * @class Roo.data.DataReader
14491  * Base class for reading structured data from a data source.  This class is intended to be
14492  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14493  */
14494
14495 Roo.data.DataReader = function(meta, recordType){
14496     
14497     this.meta = meta;
14498     
14499     this.recordType = recordType instanceof Array ? 
14500         Roo.data.Record.create(recordType) : recordType;
14501 };
14502
14503 Roo.data.DataReader.prototype = {
14504     
14505     
14506     readerType : 'Data',
14507      /**
14508      * Create an empty record
14509      * @param {Object} data (optional) - overlay some values
14510      * @return {Roo.data.Record} record created.
14511      */
14512     newRow :  function(d) {
14513         var da =  {};
14514         this.recordType.prototype.fields.each(function(c) {
14515             switch( c.type) {
14516                 case 'int' : da[c.name] = 0; break;
14517                 case 'date' : da[c.name] = new Date(); break;
14518                 case 'float' : da[c.name] = 0.0; break;
14519                 case 'boolean' : da[c.name] = false; break;
14520                 default : da[c.name] = ""; break;
14521             }
14522             
14523         });
14524         return new this.recordType(Roo.apply(da, d));
14525     }
14526     
14527     
14528 };/*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538
14539 /**
14540  * @class Roo.data.DataProxy
14541  * @extends Roo.data.Observable
14542  * This class is an abstract base class for implementations which provide retrieval of
14543  * unformatted data objects.<br>
14544  * <p>
14545  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14546  * (of the appropriate type which knows how to parse the data object) to provide a block of
14547  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14548  * <p>
14549  * Custom implementations must implement the load method as described in
14550  * {@link Roo.data.HttpProxy#load}.
14551  */
14552 Roo.data.DataProxy = function(){
14553     this.addEvents({
14554         /**
14555          * @event beforeload
14556          * Fires before a network request is made to retrieve a data object.
14557          * @param {Object} This DataProxy object.
14558          * @param {Object} params The params parameter to the load function.
14559          */
14560         beforeload : true,
14561         /**
14562          * @event load
14563          * Fires before the load method's callback is called.
14564          * @param {Object} This DataProxy object.
14565          * @param {Object} o The data object.
14566          * @param {Object} arg The callback argument object passed to the load function.
14567          */
14568         load : true,
14569         /**
14570          * @event loadexception
14571          * Fires if an Exception occurs during data retrieval.
14572          * @param {Object} This DataProxy object.
14573          * @param {Object} o The data object.
14574          * @param {Object} arg The callback argument object passed to the load function.
14575          * @param {Object} e The Exception.
14576          */
14577         loadexception : true
14578     });
14579     Roo.data.DataProxy.superclass.constructor.call(this);
14580 };
14581
14582 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14583
14584     /**
14585      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14586      */
14587 /*
14588  * Based on:
14589  * Ext JS Library 1.1.1
14590  * Copyright(c) 2006-2007, Ext JS, LLC.
14591  *
14592  * Originally Released Under LGPL - original licence link has changed is not relivant.
14593  *
14594  * Fork - LGPL
14595  * <script type="text/javascript">
14596  */
14597 /**
14598  * @class Roo.data.MemoryProxy
14599  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14600  * to the Reader when its load method is called.
14601  * @constructor
14602  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14603  */
14604 Roo.data.MemoryProxy = function(data){
14605     if (data.data) {
14606         data = data.data;
14607     }
14608     Roo.data.MemoryProxy.superclass.constructor.call(this);
14609     this.data = data;
14610 };
14611
14612 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14613     
14614     /**
14615      * Load data from the requested source (in this case an in-memory
14616      * data object passed to the constructor), read the data object into
14617      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14618      * process that block using the passed callback.
14619      * @param {Object} params This parameter is not used by the MemoryProxy class.
14620      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14621      * object into a block of Roo.data.Records.
14622      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14623      * The function must be passed <ul>
14624      * <li>The Record block object</li>
14625      * <li>The "arg" argument from the load function</li>
14626      * <li>A boolean success indicator</li>
14627      * </ul>
14628      * @param {Object} scope The scope in which to call the callback
14629      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14630      */
14631     load : function(params, reader, callback, scope, arg){
14632         params = params || {};
14633         var result;
14634         try {
14635             result = reader.readRecords(params.data ? params.data :this.data);
14636         }catch(e){
14637             this.fireEvent("loadexception", this, arg, null, e);
14638             callback.call(scope, null, arg, false);
14639             return;
14640         }
14641         callback.call(scope, result, arg, true);
14642     },
14643     
14644     // private
14645     update : function(params, records){
14646         
14647     }
14648 });/*
14649  * Based on:
14650  * Ext JS Library 1.1.1
14651  * Copyright(c) 2006-2007, Ext JS, LLC.
14652  *
14653  * Originally Released Under LGPL - original licence link has changed is not relivant.
14654  *
14655  * Fork - LGPL
14656  * <script type="text/javascript">
14657  */
14658 /**
14659  * @class Roo.data.HttpProxy
14660  * @extends Roo.data.DataProxy
14661  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14662  * configured to reference a certain URL.<br><br>
14663  * <p>
14664  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14665  * from which the running page was served.<br><br>
14666  * <p>
14667  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14668  * <p>
14669  * Be aware that to enable the browser to parse an XML document, the server must set
14670  * the Content-Type header in the HTTP response to "text/xml".
14671  * @constructor
14672  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14673  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14674  * will be used to make the request.
14675  */
14676 Roo.data.HttpProxy = function(conn){
14677     Roo.data.HttpProxy.superclass.constructor.call(this);
14678     // is conn a conn config or a real conn?
14679     this.conn = conn;
14680     this.useAjax = !conn || !conn.events;
14681   
14682 };
14683
14684 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14685     // thse are take from connection...
14686     
14687     /**
14688      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14689      */
14690     /**
14691      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14692      * extra parameters to each request made by this object. (defaults to undefined)
14693      */
14694     /**
14695      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14696      *  to each request made by this object. (defaults to undefined)
14697      */
14698     /**
14699      * @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)
14700      */
14701     /**
14702      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14703      */
14704      /**
14705      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14706      * @type Boolean
14707      */
14708   
14709
14710     /**
14711      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14712      * @type Boolean
14713      */
14714     /**
14715      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14716      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14717      * a finer-grained basis than the DataProxy events.
14718      */
14719     getConnection : function(){
14720         return this.useAjax ? Roo.Ajax : this.conn;
14721     },
14722
14723     /**
14724      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14725      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14726      * process that block using the passed callback.
14727      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14728      * for the request to the remote server.
14729      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14730      * object into a block of Roo.data.Records.
14731      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14732      * The function must be passed <ul>
14733      * <li>The Record block object</li>
14734      * <li>The "arg" argument from the load function</li>
14735      * <li>A boolean success indicator</li>
14736      * </ul>
14737      * @param {Object} scope The scope in which to call the callback
14738      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14739      */
14740     load : function(params, reader, callback, scope, arg){
14741         if(this.fireEvent("beforeload", this, params) !== false){
14742             var  o = {
14743                 params : params || {},
14744                 request: {
14745                     callback : callback,
14746                     scope : scope,
14747                     arg : arg
14748                 },
14749                 reader: reader,
14750                 callback : this.loadResponse,
14751                 scope: this
14752             };
14753             if(this.useAjax){
14754                 Roo.applyIf(o, this.conn);
14755                 if(this.activeRequest){
14756                     Roo.Ajax.abort(this.activeRequest);
14757                 }
14758                 this.activeRequest = Roo.Ajax.request(o);
14759             }else{
14760                 this.conn.request(o);
14761             }
14762         }else{
14763             callback.call(scope||this, null, arg, false);
14764         }
14765     },
14766
14767     // private
14768     loadResponse : function(o, success, response){
14769         delete this.activeRequest;
14770         if(!success){
14771             this.fireEvent("loadexception", this, o, response);
14772             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14773             return;
14774         }
14775         var result;
14776         try {
14777             result = o.reader.read(response);
14778         }catch(e){
14779             this.fireEvent("loadexception", this, o, response, e);
14780             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14781             return;
14782         }
14783         
14784         this.fireEvent("load", this, o, o.request.arg);
14785         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14786     },
14787
14788     // private
14789     update : function(dataSet){
14790
14791     },
14792
14793     // private
14794     updateResponse : function(dataSet){
14795
14796     }
14797 });/*
14798  * Based on:
14799  * Ext JS Library 1.1.1
14800  * Copyright(c) 2006-2007, Ext JS, LLC.
14801  *
14802  * Originally Released Under LGPL - original licence link has changed is not relivant.
14803  *
14804  * Fork - LGPL
14805  * <script type="text/javascript">
14806  */
14807
14808 /**
14809  * @class Roo.data.ScriptTagProxy
14810  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14811  * other than the originating domain of the running page.<br><br>
14812  * <p>
14813  * <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
14814  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14815  * <p>
14816  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14817  * source code that is used as the source inside a &lt;script> tag.<br><br>
14818  * <p>
14819  * In order for the browser to process the returned data, the server must wrap the data object
14820  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14821  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14822  * depending on whether the callback name was passed:
14823  * <p>
14824  * <pre><code>
14825 boolean scriptTag = false;
14826 String cb = request.getParameter("callback");
14827 if (cb != null) {
14828     scriptTag = true;
14829     response.setContentType("text/javascript");
14830 } else {
14831     response.setContentType("application/x-json");
14832 }
14833 Writer out = response.getWriter();
14834 if (scriptTag) {
14835     out.write(cb + "(");
14836 }
14837 out.print(dataBlock.toJsonString());
14838 if (scriptTag) {
14839     out.write(");");
14840 }
14841 </pre></code>
14842  *
14843  * @constructor
14844  * @param {Object} config A configuration object.
14845  */
14846 Roo.data.ScriptTagProxy = function(config){
14847     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14848     Roo.apply(this, config);
14849     this.head = document.getElementsByTagName("head")[0];
14850 };
14851
14852 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14853
14854 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14855     /**
14856      * @cfg {String} url The URL from which to request the data object.
14857      */
14858     /**
14859      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14860      */
14861     timeout : 30000,
14862     /**
14863      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14864      * the server the name of the callback function set up by the load call to process the returned data object.
14865      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14866      * javascript output which calls this named function passing the data object as its only parameter.
14867      */
14868     callbackParam : "callback",
14869     /**
14870      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14871      * name to the request.
14872      */
14873     nocache : true,
14874
14875     /**
14876      * Load data from the configured URL, read the data object into
14877      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14878      * process that block using the passed callback.
14879      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14880      * for the request to the remote server.
14881      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14882      * object into a block of Roo.data.Records.
14883      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14884      * The function must be passed <ul>
14885      * <li>The Record block object</li>
14886      * <li>The "arg" argument from the load function</li>
14887      * <li>A boolean success indicator</li>
14888      * </ul>
14889      * @param {Object} scope The scope in which to call the callback
14890      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14891      */
14892     load : function(params, reader, callback, scope, arg){
14893         if(this.fireEvent("beforeload", this, params) !== false){
14894
14895             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14896
14897             var url = this.url;
14898             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14899             if(this.nocache){
14900                 url += "&_dc=" + (new Date().getTime());
14901             }
14902             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14903             var trans = {
14904                 id : transId,
14905                 cb : "stcCallback"+transId,
14906                 scriptId : "stcScript"+transId,
14907                 params : params,
14908                 arg : arg,
14909                 url : url,
14910                 callback : callback,
14911                 scope : scope,
14912                 reader : reader
14913             };
14914             var conn = this;
14915
14916             window[trans.cb] = function(o){
14917                 conn.handleResponse(o, trans);
14918             };
14919
14920             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14921
14922             if(this.autoAbort !== false){
14923                 this.abort();
14924             }
14925
14926             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14927
14928             var script = document.createElement("script");
14929             script.setAttribute("src", url);
14930             script.setAttribute("type", "text/javascript");
14931             script.setAttribute("id", trans.scriptId);
14932             this.head.appendChild(script);
14933
14934             this.trans = trans;
14935         }else{
14936             callback.call(scope||this, null, arg, false);
14937         }
14938     },
14939
14940     // private
14941     isLoading : function(){
14942         return this.trans ? true : false;
14943     },
14944
14945     /**
14946      * Abort the current server request.
14947      */
14948     abort : function(){
14949         if(this.isLoading()){
14950             this.destroyTrans(this.trans);
14951         }
14952     },
14953
14954     // private
14955     destroyTrans : function(trans, isLoaded){
14956         this.head.removeChild(document.getElementById(trans.scriptId));
14957         clearTimeout(trans.timeoutId);
14958         if(isLoaded){
14959             window[trans.cb] = undefined;
14960             try{
14961                 delete window[trans.cb];
14962             }catch(e){}
14963         }else{
14964             // if hasn't been loaded, wait for load to remove it to prevent script error
14965             window[trans.cb] = function(){
14966                 window[trans.cb] = undefined;
14967                 try{
14968                     delete window[trans.cb];
14969                 }catch(e){}
14970             };
14971         }
14972     },
14973
14974     // private
14975     handleResponse : function(o, trans){
14976         this.trans = false;
14977         this.destroyTrans(trans, true);
14978         var result;
14979         try {
14980             result = trans.reader.readRecords(o);
14981         }catch(e){
14982             this.fireEvent("loadexception", this, o, trans.arg, e);
14983             trans.callback.call(trans.scope||window, null, trans.arg, false);
14984             return;
14985         }
14986         this.fireEvent("load", this, o, trans.arg);
14987         trans.callback.call(trans.scope||window, result, trans.arg, true);
14988     },
14989
14990     // private
14991     handleFailure : function(trans){
14992         this.trans = false;
14993         this.destroyTrans(trans, false);
14994         this.fireEvent("loadexception", this, null, trans.arg);
14995         trans.callback.call(trans.scope||window, null, trans.arg, false);
14996     }
14997 });/*
14998  * Based on:
14999  * Ext JS Library 1.1.1
15000  * Copyright(c) 2006-2007, Ext JS, LLC.
15001  *
15002  * Originally Released Under LGPL - original licence link has changed is not relivant.
15003  *
15004  * Fork - LGPL
15005  * <script type="text/javascript">
15006  */
15007
15008 /**
15009  * @class Roo.data.JsonReader
15010  * @extends Roo.data.DataReader
15011  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15012  * based on mappings in a provided Roo.data.Record constructor.
15013  * 
15014  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15015  * in the reply previously. 
15016  * 
15017  * <p>
15018  * Example code:
15019  * <pre><code>
15020 var RecordDef = Roo.data.Record.create([
15021     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15022     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15023 ]);
15024 var myReader = new Roo.data.JsonReader({
15025     totalProperty: "results",    // The property which contains the total dataset size (optional)
15026     root: "rows",                // The property which contains an Array of row objects
15027     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15028 }, RecordDef);
15029 </code></pre>
15030  * <p>
15031  * This would consume a JSON file like this:
15032  * <pre><code>
15033 { 'results': 2, 'rows': [
15034     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15035     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15036 }
15037 </code></pre>
15038  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15039  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15040  * paged from the remote server.
15041  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15042  * @cfg {String} root name of the property which contains the Array of row objects.
15043  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15044  * @cfg {Array} fields Array of field definition objects
15045  * @constructor
15046  * Create a new JsonReader
15047  * @param {Object} meta Metadata configuration options
15048  * @param {Object} recordType Either an Array of field definition objects,
15049  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15050  */
15051 Roo.data.JsonReader = function(meta, recordType){
15052     
15053     meta = meta || {};
15054     // set some defaults:
15055     Roo.applyIf(meta, {
15056         totalProperty: 'total',
15057         successProperty : 'success',
15058         root : 'data',
15059         id : 'id'
15060     });
15061     
15062     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15063 };
15064 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15065     
15066     readerType : 'Json',
15067     
15068     /**
15069      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15070      * Used by Store query builder to append _requestMeta to params.
15071      * 
15072      */
15073     metaFromRemote : false,
15074     /**
15075      * This method is only used by a DataProxy which has retrieved data from a remote server.
15076      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15077      * @return {Object} data A data block which is used by an Roo.data.Store object as
15078      * a cache of Roo.data.Records.
15079      */
15080     read : function(response){
15081         var json = response.responseText;
15082        
15083         var o = /* eval:var:o */ eval("("+json+")");
15084         if(!o) {
15085             throw {message: "JsonReader.read: Json object not found"};
15086         }
15087         
15088         if(o.metaData){
15089             
15090             delete this.ef;
15091             this.metaFromRemote = true;
15092             this.meta = o.metaData;
15093             this.recordType = Roo.data.Record.create(o.metaData.fields);
15094             this.onMetaChange(this.meta, this.recordType, o);
15095         }
15096         return this.readRecords(o);
15097     },
15098
15099     // private function a store will implement
15100     onMetaChange : function(meta, recordType, o){
15101
15102     },
15103
15104     /**
15105          * @ignore
15106          */
15107     simpleAccess: function(obj, subsc) {
15108         return obj[subsc];
15109     },
15110
15111         /**
15112          * @ignore
15113          */
15114     getJsonAccessor: function(){
15115         var re = /[\[\.]/;
15116         return function(expr) {
15117             try {
15118                 return(re.test(expr))
15119                     ? new Function("obj", "return obj." + expr)
15120                     : function(obj){
15121                         return obj[expr];
15122                     };
15123             } catch(e){}
15124             return Roo.emptyFn;
15125         };
15126     }(),
15127
15128     /**
15129      * Create a data block containing Roo.data.Records from an XML document.
15130      * @param {Object} o An object which contains an Array of row objects in the property specified
15131      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15132      * which contains the total size of the dataset.
15133      * @return {Object} data A data block which is used by an Roo.data.Store object as
15134      * a cache of Roo.data.Records.
15135      */
15136     readRecords : function(o){
15137         /**
15138          * After any data loads, the raw JSON data is available for further custom processing.
15139          * @type Object
15140          */
15141         this.o = o;
15142         var s = this.meta, Record = this.recordType,
15143             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15144
15145 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15146         if (!this.ef) {
15147             if(s.totalProperty) {
15148                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15149                 }
15150                 if(s.successProperty) {
15151                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15152                 }
15153                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15154                 if (s.id) {
15155                         var g = this.getJsonAccessor(s.id);
15156                         this.getId = function(rec) {
15157                                 var r = g(rec);  
15158                                 return (r === undefined || r === "") ? null : r;
15159                         };
15160                 } else {
15161                         this.getId = function(){return null;};
15162                 }
15163             this.ef = [];
15164             for(var jj = 0; jj < fl; jj++){
15165                 f = fi[jj];
15166                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15167                 this.ef[jj] = this.getJsonAccessor(map);
15168             }
15169         }
15170
15171         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15172         if(s.totalProperty){
15173             var vt = parseInt(this.getTotal(o), 10);
15174             if(!isNaN(vt)){
15175                 totalRecords = vt;
15176             }
15177         }
15178         if(s.successProperty){
15179             var vs = this.getSuccess(o);
15180             if(vs === false || vs === 'false'){
15181                 success = false;
15182             }
15183         }
15184         var records = [];
15185         for(var i = 0; i < c; i++){
15186                 var n = root[i];
15187             var values = {};
15188             var id = this.getId(n);
15189             for(var j = 0; j < fl; j++){
15190                 f = fi[j];
15191             var v = this.ef[j](n);
15192             if (!f.convert) {
15193                 Roo.log('missing convert for ' + f.name);
15194                 Roo.log(f);
15195                 continue;
15196             }
15197             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15198             }
15199             var record = new Record(values, id);
15200             record.json = n;
15201             records[i] = record;
15202         }
15203         return {
15204             raw : o,
15205             success : success,
15206             records : records,
15207             totalRecords : totalRecords
15208         };
15209     },
15210     // used when loading children.. @see loadDataFromChildren
15211     toLoadData: function(rec)
15212     {
15213         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15214         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15215         return { data : data, total : data.length };
15216         
15217     }
15218 });/*
15219  * Based on:
15220  * Ext JS Library 1.1.1
15221  * Copyright(c) 2006-2007, Ext JS, LLC.
15222  *
15223  * Originally Released Under LGPL - original licence link has changed is not relivant.
15224  *
15225  * Fork - LGPL
15226  * <script type="text/javascript">
15227  */
15228
15229 /**
15230  * @class Roo.data.ArrayReader
15231  * @extends Roo.data.DataReader
15232  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15233  * Each element of that Array represents a row of data fields. The
15234  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15235  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15236  * <p>
15237  * Example code:.
15238  * <pre><code>
15239 var RecordDef = Roo.data.Record.create([
15240     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15241     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15242 ]);
15243 var myReader = new Roo.data.ArrayReader({
15244     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15245 }, RecordDef);
15246 </code></pre>
15247  * <p>
15248  * This would consume an Array like this:
15249  * <pre><code>
15250 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15251   </code></pre>
15252  
15253  * @constructor
15254  * Create a new JsonReader
15255  * @param {Object} meta Metadata configuration options.
15256  * @param {Object|Array} recordType Either an Array of field definition objects
15257  * 
15258  * @cfg {Array} fields Array of field definition objects
15259  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15260  * as specified to {@link Roo.data.Record#create},
15261  * or an {@link Roo.data.Record} object
15262  *
15263  * 
15264  * created using {@link Roo.data.Record#create}.
15265  */
15266 Roo.data.ArrayReader = function(meta, recordType)
15267 {    
15268     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15269 };
15270
15271 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15272     
15273       /**
15274      * Create a data block containing Roo.data.Records from an XML document.
15275      * @param {Object} o An Array of row objects which represents the dataset.
15276      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15277      * a cache of Roo.data.Records.
15278      */
15279     readRecords : function(o)
15280     {
15281         var sid = this.meta ? this.meta.id : null;
15282         var recordType = this.recordType, fields = recordType.prototype.fields;
15283         var records = [];
15284         var root = o;
15285         for(var i = 0; i < root.length; i++){
15286             var n = root[i];
15287             var values = {};
15288             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15289             for(var j = 0, jlen = fields.length; j < jlen; j++){
15290                 var f = fields.items[j];
15291                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15292                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15293                 v = f.convert(v);
15294                 values[f.name] = v;
15295             }
15296             var record = new recordType(values, id);
15297             record.json = n;
15298             records[records.length] = record;
15299         }
15300         return {
15301             records : records,
15302             totalRecords : records.length
15303         };
15304     },
15305     // used when loading children.. @see loadDataFromChildren
15306     toLoadData: function(rec)
15307     {
15308         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15309         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15310         
15311     }
15312     
15313     
15314 });/*
15315  * - LGPL
15316  * * 
15317  */
15318
15319 /**
15320  * @class Roo.bootstrap.ComboBox
15321  * @extends Roo.bootstrap.TriggerField
15322  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15323  * @cfg {Boolean} append (true|false) default false
15324  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15325  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15326  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15327  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15328  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15329  * @cfg {Boolean} animate default true
15330  * @cfg {Boolean} emptyResultText only for touch device
15331  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15332  * @cfg {String} emptyTitle default ''
15333  * @cfg {Number} width fixed with? experimental
15334  * @constructor
15335  * Create a new ComboBox.
15336  * @param {Object} config Configuration options
15337  */
15338 Roo.bootstrap.ComboBox = function(config){
15339     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15340     this.addEvents({
15341         /**
15342          * @event expand
15343          * Fires when the dropdown list is expanded
15344         * @param {Roo.bootstrap.ComboBox} combo This combo box
15345         */
15346         'expand' : true,
15347         /**
15348          * @event collapse
15349          * Fires when the dropdown list is collapsed
15350         * @param {Roo.bootstrap.ComboBox} combo This combo box
15351         */
15352         'collapse' : true,
15353         /**
15354          * @event beforeselect
15355          * Fires before a list item is selected. Return false to cancel the selection.
15356         * @param {Roo.bootstrap.ComboBox} combo This combo box
15357         * @param {Roo.data.Record} record The data record returned from the underlying store
15358         * @param {Number} index The index of the selected item in the dropdown list
15359         */
15360         'beforeselect' : true,
15361         /**
15362          * @event select
15363          * Fires when a list item is selected
15364         * @param {Roo.bootstrap.ComboBox} combo This combo box
15365         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15366         * @param {Number} index The index of the selected item in the dropdown list
15367         */
15368         'select' : true,
15369         /**
15370          * @event beforequery
15371          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15372          * The event object passed has these properties:
15373         * @param {Roo.bootstrap.ComboBox} combo This combo box
15374         * @param {String} query The query
15375         * @param {Boolean} forceAll true to force "all" query
15376         * @param {Boolean} cancel true to cancel the query
15377         * @param {Object} e The query event object
15378         */
15379         'beforequery': true,
15380          /**
15381          * @event add
15382          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15383         * @param {Roo.bootstrap.ComboBox} combo This combo box
15384         */
15385         'add' : true,
15386         /**
15387          * @event edit
15388          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15389         * @param {Roo.bootstrap.ComboBox} combo This combo box
15390         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15391         */
15392         'edit' : true,
15393         /**
15394          * @event remove
15395          * Fires when the remove value from the combobox array
15396         * @param {Roo.bootstrap.ComboBox} combo This combo box
15397         */
15398         'remove' : true,
15399         /**
15400          * @event afterremove
15401          * Fires when the remove value from the combobox array
15402         * @param {Roo.bootstrap.ComboBox} combo This combo box
15403         */
15404         'afterremove' : true,
15405         /**
15406          * @event specialfilter
15407          * Fires when specialfilter
15408             * @param {Roo.bootstrap.ComboBox} combo This combo box
15409             */
15410         'specialfilter' : true,
15411         /**
15412          * @event tick
15413          * Fires when tick the element
15414             * @param {Roo.bootstrap.ComboBox} combo This combo box
15415             */
15416         'tick' : true,
15417         /**
15418          * @event touchviewdisplay
15419          * Fires when touch view require special display (default is using displayField)
15420             * @param {Roo.bootstrap.ComboBox} combo This combo box
15421             * @param {Object} cfg set html .
15422             */
15423         'touchviewdisplay' : true
15424         
15425     });
15426     
15427     this.item = [];
15428     this.tickItems = [];
15429     
15430     this.selectedIndex = -1;
15431     if(this.mode == 'local'){
15432         if(config.queryDelay === undefined){
15433             this.queryDelay = 10;
15434         }
15435         if(config.minChars === undefined){
15436             this.minChars = 0;
15437         }
15438     }
15439 };
15440
15441 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15442      
15443     /**
15444      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15445      * rendering into an Roo.Editor, defaults to false)
15446      */
15447     /**
15448      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15449      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15450      */
15451     /**
15452      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15453      */
15454     /**
15455      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15456      * the dropdown list (defaults to undefined, with no header element)
15457      */
15458
15459      /**
15460      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15461      */
15462      
15463      /**
15464      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15465      */
15466     listWidth: undefined,
15467     /**
15468      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15469      * mode = 'remote' or 'text' if mode = 'local')
15470      */
15471     displayField: undefined,
15472     
15473     /**
15474      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15475      * mode = 'remote' or 'value' if mode = 'local'). 
15476      * Note: use of a valueField requires the user make a selection
15477      * in order for a value to be mapped.
15478      */
15479     valueField: undefined,
15480     /**
15481      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15482      */
15483     modalTitle : '',
15484     
15485     /**
15486      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15487      * field's data value (defaults to the underlying DOM element's name)
15488      */
15489     hiddenName: undefined,
15490     /**
15491      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15492      */
15493     listClass: '',
15494     /**
15495      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15496      */
15497     selectedClass: 'active',
15498     
15499     /**
15500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15501      */
15502     shadow:'sides',
15503     /**
15504      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15505      * anchor positions (defaults to 'tl-bl')
15506      */
15507     listAlign: 'tl-bl?',
15508     /**
15509      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15510      */
15511     maxHeight: 300,
15512     /**
15513      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15514      * query specified by the allQuery config option (defaults to 'query')
15515      */
15516     triggerAction: 'query',
15517     /**
15518      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15519      * (defaults to 4, does not apply if editable = false)
15520      */
15521     minChars : 4,
15522     /**
15523      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15524      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15525      */
15526     typeAhead: false,
15527     /**
15528      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15529      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15530      */
15531     queryDelay: 500,
15532     /**
15533      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15534      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15535      */
15536     pageSize: 0,
15537     /**
15538      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15539      * when editable = true (defaults to false)
15540      */
15541     selectOnFocus:false,
15542     /**
15543      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15544      */
15545     queryParam: 'query',
15546     /**
15547      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15548      * when mode = 'remote' (defaults to 'Loading...')
15549      */
15550     loadingText: 'Loading...',
15551     /**
15552      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15553      */
15554     resizable: false,
15555     /**
15556      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15557      */
15558     handleHeight : 8,
15559     /**
15560      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15561      * traditional select (defaults to true)
15562      */
15563     editable: true,
15564     /**
15565      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15566      */
15567     allQuery: '',
15568     /**
15569      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15570      */
15571     mode: 'remote',
15572     /**
15573      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15574      * listWidth has a higher value)
15575      */
15576     minListWidth : 70,
15577     /**
15578      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15579      * allow the user to set arbitrary text into the field (defaults to false)
15580      */
15581     forceSelection:false,
15582     /**
15583      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15584      * if typeAhead = true (defaults to 250)
15585      */
15586     typeAheadDelay : 250,
15587     /**
15588      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15589      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15590      */
15591     valueNotFoundText : undefined,
15592     /**
15593      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15594      */
15595     blockFocus : false,
15596     
15597     /**
15598      * @cfg {Boolean} disableClear Disable showing of clear button.
15599      */
15600     disableClear : false,
15601     /**
15602      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15603      */
15604     alwaysQuery : false,
15605     
15606     /**
15607      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15608      */
15609     multiple : false,
15610     
15611     /**
15612      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15613      */
15614     invalidClass : "has-warning",
15615     
15616     /**
15617      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15618      */
15619     validClass : "has-success",
15620     
15621     /**
15622      * @cfg {Boolean} specialFilter (true|false) special filter default false
15623      */
15624     specialFilter : false,
15625     
15626     /**
15627      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15628      */
15629     mobileTouchView : true,
15630     
15631     /**
15632      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15633      */
15634     useNativeIOS : false,
15635     
15636     /**
15637      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15638      */
15639     mobile_restrict_height : false,
15640     
15641     ios_options : false,
15642     
15643     //private
15644     addicon : false,
15645     editicon: false,
15646     
15647     page: 0,
15648     hasQuery: false,
15649     append: false,
15650     loadNext: false,
15651     autoFocus : true,
15652     tickable : false,
15653     btnPosition : 'right',
15654     triggerList : true,
15655     showToggleBtn : true,
15656     animate : true,
15657     emptyResultText: 'Empty',
15658     triggerText : 'Select',
15659     emptyTitle : '',
15660     width : false,
15661     
15662     // element that contains real text value.. (when hidden is used..)
15663     
15664     getAutoCreate : function()
15665     {   
15666         var cfg = false;
15667         //render
15668         /*
15669          * Render classic select for iso
15670          */
15671         
15672         if(Roo.isIOS && this.useNativeIOS){
15673             cfg = this.getAutoCreateNativeIOS();
15674             return cfg;
15675         }
15676         
15677         /*
15678          * Touch Devices
15679          */
15680         
15681         if(Roo.isTouch && this.mobileTouchView){
15682             cfg = this.getAutoCreateTouchView();
15683             return cfg;;
15684         }
15685         
15686         /*
15687          *  Normal ComboBox
15688          */
15689         if(!this.tickable){
15690             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15691             return cfg;
15692         }
15693         
15694         /*
15695          *  ComboBox with tickable selections
15696          */
15697              
15698         var align = this.labelAlign || this.parentLabelAlign();
15699         
15700         cfg = {
15701             cls : 'form-group roo-combobox-tickable' //input-group
15702         };
15703         
15704         var btn_text_select = '';
15705         var btn_text_done = '';
15706         var btn_text_cancel = '';
15707         
15708         if (this.btn_text_show) {
15709             btn_text_select = 'Select';
15710             btn_text_done = 'Done';
15711             btn_text_cancel = 'Cancel'; 
15712         }
15713         
15714         var buttons = {
15715             tag : 'div',
15716             cls : 'tickable-buttons',
15717             cn : [
15718                 {
15719                     tag : 'button',
15720                     type : 'button',
15721                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15722                     //html : this.triggerText
15723                     html: btn_text_select
15724                 },
15725                 {
15726                     tag : 'button',
15727                     type : 'button',
15728                     name : 'ok',
15729                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15730                     //html : 'Done'
15731                     html: btn_text_done
15732                 },
15733                 {
15734                     tag : 'button',
15735                     type : 'button',
15736                     name : 'cancel',
15737                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15738                     //html : 'Cancel'
15739                     html: btn_text_cancel
15740                 }
15741             ]
15742         };
15743         
15744         if(this.editable){
15745             buttons.cn.unshift({
15746                 tag: 'input',
15747                 cls: 'roo-select2-search-field-input'
15748             });
15749         }
15750         
15751         var _this = this;
15752         
15753         Roo.each(buttons.cn, function(c){
15754             if (_this.size) {
15755                 c.cls += ' btn-' + _this.size;
15756             }
15757
15758             if (_this.disabled) {
15759                 c.disabled = true;
15760             }
15761         });
15762         
15763         var box = {
15764             tag: 'div',
15765             style : 'display: contents',
15766             cn: [
15767                 {
15768                     tag: 'input',
15769                     type : 'hidden',
15770                     cls: 'form-hidden-field'
15771                 },
15772                 {
15773                     tag: 'ul',
15774                     cls: 'roo-select2-choices',
15775                     cn:[
15776                         {
15777                             tag: 'li',
15778                             cls: 'roo-select2-search-field',
15779                             cn: [
15780                                 buttons
15781                             ]
15782                         }
15783                     ]
15784                 }
15785             ]
15786         };
15787         
15788         var combobox = {
15789             cls: 'roo-select2-container input-group roo-select2-container-multi',
15790             cn: [
15791                 
15792                 box
15793 //                {
15794 //                    tag: 'ul',
15795 //                    cls: 'typeahead typeahead-long dropdown-menu',
15796 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15797 //                }
15798             ]
15799         };
15800         
15801         if(this.hasFeedback && !this.allowBlank){
15802             
15803             var feedback = {
15804                 tag: 'span',
15805                 cls: 'glyphicon form-control-feedback'
15806             };
15807
15808             combobox.cn.push(feedback);
15809         }
15810         
15811         
15812         
15813         var indicator = {
15814             tag : 'i',
15815             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15816             tooltip : 'This field is required'
15817         };
15818         if (Roo.bootstrap.version == 4) {
15819             indicator = {
15820                 tag : 'i',
15821                 style : 'display:none'
15822             };
15823         }
15824         if (align ==='left' && this.fieldLabel.length) {
15825             
15826             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15827             
15828             cfg.cn = [
15829                 indicator,
15830                 {
15831                     tag: 'label',
15832                     'for' :  id,
15833                     cls : 'control-label col-form-label',
15834                     html : this.fieldLabel
15835
15836                 },
15837                 {
15838                     cls : "", 
15839                     cn: [
15840                         combobox
15841                     ]
15842                 }
15843
15844             ];
15845             
15846             var labelCfg = cfg.cn[1];
15847             var contentCfg = cfg.cn[2];
15848             
15849
15850             if(this.indicatorpos == 'right'){
15851                 
15852                 cfg.cn = [
15853                     {
15854                         tag: 'label',
15855                         'for' :  id,
15856                         cls : 'control-label col-form-label',
15857                         cn : [
15858                             {
15859                                 tag : 'span',
15860                                 html : this.fieldLabel
15861                             },
15862                             indicator
15863                         ]
15864                     },
15865                     {
15866                         cls : "",
15867                         cn: [
15868                             combobox
15869                         ]
15870                     }
15871
15872                 ];
15873                 
15874                 
15875                 
15876                 labelCfg = cfg.cn[0];
15877                 contentCfg = cfg.cn[1];
15878             
15879             }
15880             
15881             if(this.labelWidth > 12){
15882                 labelCfg.style = "width: " + this.labelWidth + 'px';
15883             }
15884             if(this.width * 1 > 0){
15885                 contentCfg.style = "width: " + this.width + 'px';
15886             }
15887             if(this.labelWidth < 13 && this.labelmd == 0){
15888                 this.labelmd = this.labelWidth;
15889             }
15890             
15891             if(this.labellg > 0){
15892                 labelCfg.cls += ' col-lg-' + this.labellg;
15893                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15894             }
15895             
15896             if(this.labelmd > 0){
15897                 labelCfg.cls += ' col-md-' + this.labelmd;
15898                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15899             }
15900             
15901             if(this.labelsm > 0){
15902                 labelCfg.cls += ' col-sm-' + this.labelsm;
15903                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15904             }
15905             
15906             if(this.labelxs > 0){
15907                 labelCfg.cls += ' col-xs-' + this.labelxs;
15908                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15909             }
15910                 
15911                 
15912         } else if ( this.fieldLabel.length) {
15913 //                Roo.log(" label");
15914                  cfg.cn = [
15915                    indicator,
15916                     {
15917                         tag: 'label',
15918                         //cls : 'input-group-addon',
15919                         html : this.fieldLabel
15920                     },
15921                     combobox
15922                 ];
15923                 
15924                 if(this.indicatorpos == 'right'){
15925                     cfg.cn = [
15926                         {
15927                             tag: 'label',
15928                             //cls : 'input-group-addon',
15929                             html : this.fieldLabel
15930                         },
15931                         indicator,
15932                         combobox
15933                     ];
15934                     
15935                 }
15936
15937         } else {
15938             
15939 //                Roo.log(" no label && no align");
15940                 cfg = combobox
15941                      
15942                 
15943         }
15944          
15945         var settings=this;
15946         ['xs','sm','md','lg'].map(function(size){
15947             if (settings[size]) {
15948                 cfg.cls += ' col-' + size + '-' + settings[size];
15949             }
15950         });
15951         
15952         return cfg;
15953         
15954     },
15955     
15956     _initEventsCalled : false,
15957     
15958     // private
15959     initEvents: function()
15960     {   
15961         if (this._initEventsCalled) { // as we call render... prevent looping...
15962             return;
15963         }
15964         this._initEventsCalled = true;
15965         
15966         if (!this.store) {
15967             throw "can not find store for combo";
15968         }
15969         
15970         this.indicator = this.indicatorEl();
15971         
15972         this.store = Roo.factory(this.store, Roo.data);
15973         this.store.parent = this;
15974         
15975         // if we are building from html. then this element is so complex, that we can not really
15976         // use the rendered HTML.
15977         // so we have to trash and replace the previous code.
15978         if (Roo.XComponent.build_from_html) {
15979             // remove this element....
15980             var e = this.el.dom, k=0;
15981             while (e ) { e = e.previousSibling;  ++k;}
15982
15983             this.el.remove();
15984             
15985             this.el=false;
15986             this.rendered = false;
15987             
15988             this.render(this.parent().getChildContainer(true), k);
15989         }
15990         
15991         if(Roo.isIOS && this.useNativeIOS){
15992             this.initIOSView();
15993             return;
15994         }
15995         
15996         /*
15997          * Touch Devices
15998          */
15999         
16000         if(Roo.isTouch && this.mobileTouchView){
16001             this.initTouchView();
16002             return;
16003         }
16004         
16005         if(this.tickable){
16006             this.initTickableEvents();
16007             return;
16008         }
16009         
16010         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16011         
16012         if(this.hiddenName){
16013             
16014             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16015             
16016             this.hiddenField.dom.value =
16017                 this.hiddenValue !== undefined ? this.hiddenValue :
16018                 this.value !== undefined ? this.value : '';
16019
16020             // prevent input submission
16021             this.el.dom.removeAttribute('name');
16022             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16023              
16024              
16025         }
16026         //if(Roo.isGecko){
16027         //    this.el.dom.setAttribute('autocomplete', 'off');
16028         //}
16029         
16030         var cls = 'x-combo-list';
16031         
16032         //this.list = new Roo.Layer({
16033         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16034         //});
16035         
16036         var _this = this;
16037         
16038         (function(){
16039             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16040             _this.list.setWidth(lw);
16041         }).defer(100);
16042         
16043         this.list.on('mouseover', this.onViewOver, this);
16044         this.list.on('mousemove', this.onViewMove, this);
16045         this.list.on('scroll', this.onViewScroll, this);
16046         
16047         /*
16048         this.list.swallowEvent('mousewheel');
16049         this.assetHeight = 0;
16050
16051         if(this.title){
16052             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16053             this.assetHeight += this.header.getHeight();
16054         }
16055
16056         this.innerList = this.list.createChild({cls:cls+'-inner'});
16057         this.innerList.on('mouseover', this.onViewOver, this);
16058         this.innerList.on('mousemove', this.onViewMove, this);
16059         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16060         
16061         if(this.allowBlank && !this.pageSize && !this.disableClear){
16062             this.footer = this.list.createChild({cls:cls+'-ft'});
16063             this.pageTb = new Roo.Toolbar(this.footer);
16064            
16065         }
16066         if(this.pageSize){
16067             this.footer = this.list.createChild({cls:cls+'-ft'});
16068             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16069                     {pageSize: this.pageSize});
16070             
16071         }
16072         
16073         if (this.pageTb && this.allowBlank && !this.disableClear) {
16074             var _this = this;
16075             this.pageTb.add(new Roo.Toolbar.Fill(), {
16076                 cls: 'x-btn-icon x-btn-clear',
16077                 text: '&#160;',
16078                 handler: function()
16079                 {
16080                     _this.collapse();
16081                     _this.clearValue();
16082                     _this.onSelect(false, -1);
16083                 }
16084             });
16085         }
16086         if (this.footer) {
16087             this.assetHeight += this.footer.getHeight();
16088         }
16089         */
16090             
16091         if(!this.tpl){
16092             this.tpl = Roo.bootstrap.version == 4 ?
16093                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16094                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16095         }
16096
16097         this.view = new Roo.View(this.list, this.tpl, {
16098             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16099         });
16100         //this.view.wrapEl.setDisplayed(false);
16101         this.view.on('click', this.onViewClick, this);
16102         
16103         
16104         this.store.on('beforeload', this.onBeforeLoad, this);
16105         this.store.on('load', this.onLoad, this);
16106         this.store.on('loadexception', this.onLoadException, this);
16107         /*
16108         if(this.resizable){
16109             this.resizer = new Roo.Resizable(this.list,  {
16110                pinned:true, handles:'se'
16111             });
16112             this.resizer.on('resize', function(r, w, h){
16113                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16114                 this.listWidth = w;
16115                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16116                 this.restrictHeight();
16117             }, this);
16118             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16119         }
16120         */
16121         if(!this.editable){
16122             this.editable = true;
16123             this.setEditable(false);
16124         }
16125         
16126         /*
16127         
16128         if (typeof(this.events.add.listeners) != 'undefined') {
16129             
16130             this.addicon = this.wrap.createChild(
16131                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16132        
16133             this.addicon.on('click', function(e) {
16134                 this.fireEvent('add', this);
16135             }, this);
16136         }
16137         if (typeof(this.events.edit.listeners) != 'undefined') {
16138             
16139             this.editicon = this.wrap.createChild(
16140                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16141             if (this.addicon) {
16142                 this.editicon.setStyle('margin-left', '40px');
16143             }
16144             this.editicon.on('click', function(e) {
16145                 
16146                 // we fire even  if inothing is selected..
16147                 this.fireEvent('edit', this, this.lastData );
16148                 
16149             }, this);
16150         }
16151         */
16152         
16153         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16154             "up" : function(e){
16155                 this.inKeyMode = true;
16156                 this.selectPrev();
16157             },
16158
16159             "down" : function(e){
16160                 if(!this.isExpanded()){
16161                     this.onTriggerClick();
16162                 }else{
16163                     this.inKeyMode = true;
16164                     this.selectNext();
16165                 }
16166             },
16167
16168             "enter" : function(e){
16169 //                this.onViewClick();
16170                 //return true;
16171                 this.collapse();
16172                 
16173                 if(this.fireEvent("specialkey", this, e)){
16174                     this.onViewClick(false);
16175                 }
16176                 
16177                 return true;
16178             },
16179
16180             "esc" : function(e){
16181                 this.collapse();
16182             },
16183
16184             "tab" : function(e){
16185                 this.collapse();
16186                 
16187                 if(this.fireEvent("specialkey", this, e)){
16188                     this.onViewClick(false);
16189                 }
16190                 
16191                 return true;
16192             },
16193
16194             scope : this,
16195
16196             doRelay : function(foo, bar, hname){
16197                 if(hname == 'down' || this.scope.isExpanded()){
16198                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16199                 }
16200                 return true;
16201             },
16202
16203             forceKeyDown: true
16204         });
16205         
16206         
16207         this.queryDelay = Math.max(this.queryDelay || 10,
16208                 this.mode == 'local' ? 10 : 250);
16209         
16210         
16211         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16212         
16213         if(this.typeAhead){
16214             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16215         }
16216         if(this.editable !== false){
16217             this.inputEl().on("keyup", this.onKeyUp, this);
16218         }
16219         if(this.forceSelection){
16220             this.inputEl().on('blur', this.doForce, this);
16221         }
16222         
16223         if(this.multiple){
16224             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16225             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16226         }
16227     },
16228     
16229     initTickableEvents: function()
16230     {   
16231         this.createList();
16232         
16233         if(this.hiddenName){
16234             
16235             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16236             
16237             this.hiddenField.dom.value =
16238                 this.hiddenValue !== undefined ? this.hiddenValue :
16239                 this.value !== undefined ? this.value : '';
16240
16241             // prevent input submission
16242             this.el.dom.removeAttribute('name');
16243             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16244              
16245              
16246         }
16247         
16248 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16249         
16250         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16251         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16252         if(this.triggerList){
16253             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16254         }
16255          
16256         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16257         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16258         
16259         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16260         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16261         
16262         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16263         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16264         
16265         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16266         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16267         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16268         
16269         this.okBtn.hide();
16270         this.cancelBtn.hide();
16271         
16272         var _this = this;
16273         
16274         (function(){
16275             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16276             _this.list.setWidth(lw);
16277         }).defer(100);
16278         
16279         this.list.on('mouseover', this.onViewOver, this);
16280         this.list.on('mousemove', this.onViewMove, this);
16281         
16282         this.list.on('scroll', this.onViewScroll, this);
16283         
16284         if(!this.tpl){
16285             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16286                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16287         }
16288
16289         this.view = new Roo.View(this.list, this.tpl, {
16290             singleSelect:true,
16291             tickable:true,
16292             parent:this,
16293             store: this.store,
16294             selectedClass: this.selectedClass
16295         });
16296         
16297         //this.view.wrapEl.setDisplayed(false);
16298         this.view.on('click', this.onViewClick, this);
16299         
16300         
16301         
16302         this.store.on('beforeload', this.onBeforeLoad, this);
16303         this.store.on('load', this.onLoad, this);
16304         this.store.on('loadexception', this.onLoadException, this);
16305         
16306         if(this.editable){
16307             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16308                 "up" : function(e){
16309                     this.inKeyMode = true;
16310                     this.selectPrev();
16311                 },
16312
16313                 "down" : function(e){
16314                     this.inKeyMode = true;
16315                     this.selectNext();
16316                 },
16317
16318                 "enter" : function(e){
16319                     if(this.fireEvent("specialkey", this, e)){
16320                         this.onViewClick(false);
16321                     }
16322                     
16323                     return true;
16324                 },
16325
16326                 "esc" : function(e){
16327                     this.onTickableFooterButtonClick(e, false, false);
16328                 },
16329
16330                 "tab" : function(e){
16331                     this.fireEvent("specialkey", this, e);
16332                     
16333                     this.onTickableFooterButtonClick(e, false, false);
16334                     
16335                     return true;
16336                 },
16337
16338                 scope : this,
16339
16340                 doRelay : function(e, fn, key){
16341                     if(this.scope.isExpanded()){
16342                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16343                     }
16344                     return true;
16345                 },
16346
16347                 forceKeyDown: true
16348             });
16349         }
16350         
16351         this.queryDelay = Math.max(this.queryDelay || 10,
16352                 this.mode == 'local' ? 10 : 250);
16353         
16354         
16355         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16356         
16357         if(this.typeAhead){
16358             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16359         }
16360         
16361         if(this.editable !== false){
16362             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16363         }
16364         
16365         this.indicator = this.indicatorEl();
16366         
16367         if(this.indicator){
16368             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16369             this.indicator.hide();
16370         }
16371         
16372     },
16373
16374     onDestroy : function(){
16375         if(this.view){
16376             this.view.setStore(null);
16377             this.view.el.removeAllListeners();
16378             this.view.el.remove();
16379             this.view.purgeListeners();
16380         }
16381         if(this.list){
16382             this.list.dom.innerHTML  = '';
16383         }
16384         
16385         if(this.store){
16386             this.store.un('beforeload', this.onBeforeLoad, this);
16387             this.store.un('load', this.onLoad, this);
16388             this.store.un('loadexception', this.onLoadException, this);
16389         }
16390         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16391     },
16392
16393     // private
16394     fireKey : function(e){
16395         if(e.isNavKeyPress() && !this.list.isVisible()){
16396             this.fireEvent("specialkey", this, e);
16397         }
16398     },
16399
16400     // private
16401     onResize: function(w, h)
16402     {
16403         
16404         
16405 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16406 //        
16407 //        if(typeof w != 'number'){
16408 //            // we do not handle it!?!?
16409 //            return;
16410 //        }
16411 //        var tw = this.trigger.getWidth();
16412 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16413 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16414 //        var x = w - tw;
16415 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16416 //            
16417 //        //this.trigger.setStyle('left', x+'px');
16418 //        
16419 //        if(this.list && this.listWidth === undefined){
16420 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16421 //            this.list.setWidth(lw);
16422 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16423 //        }
16424         
16425     
16426         
16427     },
16428
16429     /**
16430      * Allow or prevent the user from directly editing the field text.  If false is passed,
16431      * the user will only be able to select from the items defined in the dropdown list.  This method
16432      * is the runtime equivalent of setting the 'editable' config option at config time.
16433      * @param {Boolean} value True to allow the user to directly edit the field text
16434      */
16435     setEditable : function(value){
16436         if(value == this.editable){
16437             return;
16438         }
16439         this.editable = value;
16440         if(!value){
16441             this.inputEl().dom.setAttribute('readOnly', true);
16442             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16443             this.inputEl().addClass('x-combo-noedit');
16444         }else{
16445             this.inputEl().dom.removeAttribute('readOnly');
16446             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16447             this.inputEl().removeClass('x-combo-noedit');
16448         }
16449     },
16450
16451     // private
16452     
16453     onBeforeLoad : function(combo,opts){
16454         if(!this.hasFocus){
16455             return;
16456         }
16457          if (!opts.add) {
16458             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16459          }
16460         this.restrictHeight();
16461         this.selectedIndex = -1;
16462     },
16463
16464     // private
16465     onLoad : function(){
16466         
16467         this.hasQuery = false;
16468         
16469         if(!this.hasFocus){
16470             return;
16471         }
16472         
16473         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16474             this.loading.hide();
16475         }
16476         
16477         if(this.store.getCount() > 0){
16478             
16479             this.expand();
16480             this.restrictHeight();
16481             if(this.lastQuery == this.allQuery){
16482                 if(this.editable && !this.tickable){
16483                     this.inputEl().dom.select();
16484                 }
16485                 
16486                 if(
16487                     !this.selectByValue(this.value, true) &&
16488                     this.autoFocus && 
16489                     (
16490                         !this.store.lastOptions ||
16491                         typeof(this.store.lastOptions.add) == 'undefined' || 
16492                         this.store.lastOptions.add != true
16493                     )
16494                 ){
16495                     this.select(0, true);
16496                 }
16497             }else{
16498                 if(this.autoFocus){
16499                     this.selectNext();
16500                 }
16501                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16502                     this.taTask.delay(this.typeAheadDelay);
16503                 }
16504             }
16505         }else{
16506             this.onEmptyResults();
16507         }
16508         
16509         //this.el.focus();
16510     },
16511     // private
16512     onLoadException : function()
16513     {
16514         this.hasQuery = false;
16515         
16516         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16517             this.loading.hide();
16518         }
16519         
16520         if(this.tickable && this.editable){
16521             return;
16522         }
16523         
16524         this.collapse();
16525         // only causes errors at present
16526         //Roo.log(this.store.reader.jsonData);
16527         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16528             // fixme
16529             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16530         //}
16531         
16532         
16533     },
16534     // private
16535     onTypeAhead : function(){
16536         if(this.store.getCount() > 0){
16537             var r = this.store.getAt(0);
16538             var newValue = r.data[this.displayField];
16539             var len = newValue.length;
16540             var selStart = this.getRawValue().length;
16541             
16542             if(selStart != len){
16543                 this.setRawValue(newValue);
16544                 this.selectText(selStart, newValue.length);
16545             }
16546         }
16547     },
16548
16549     // private
16550     onSelect : function(record, index){
16551         
16552         if(this.fireEvent('beforeselect', this, record, index) !== false){
16553         
16554             this.setFromData(index > -1 ? record.data : false);
16555             
16556             this.collapse();
16557             this.fireEvent('select', this, record, index);
16558         }
16559     },
16560
16561     /**
16562      * Returns the currently selected field value or empty string if no value is set.
16563      * @return {String} value The selected value
16564      */
16565     getValue : function()
16566     {
16567         if(Roo.isIOS && this.useNativeIOS){
16568             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16569         }
16570         
16571         if(this.multiple){
16572             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16573         }
16574         
16575         if(this.valueField){
16576             return typeof this.value != 'undefined' ? this.value : '';
16577         }else{
16578             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16579         }
16580     },
16581     
16582     getRawValue : function()
16583     {
16584         if(Roo.isIOS && this.useNativeIOS){
16585             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16586         }
16587         
16588         var v = this.inputEl().getValue();
16589         
16590         return v;
16591     },
16592
16593     /**
16594      * Clears any text/value currently set in the field
16595      */
16596     clearValue : function(){
16597         
16598         if(this.hiddenField){
16599             this.hiddenField.dom.value = '';
16600         }
16601         this.value = '';
16602         this.setRawValue('');
16603         this.lastSelectionText = '';
16604         this.lastData = false;
16605         
16606         var close = this.closeTriggerEl();
16607         
16608         if(close){
16609             close.hide();
16610         }
16611         
16612         this.validate();
16613         
16614     },
16615
16616     /**
16617      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16618      * will be displayed in the field.  If the value does not match the data value of an existing item,
16619      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16620      * Otherwise the field will be blank (although the value will still be set).
16621      * @param {String} value The value to match
16622      */
16623     setValue : function(v)
16624     {
16625         if(Roo.isIOS && this.useNativeIOS){
16626             this.setIOSValue(v);
16627             return;
16628         }
16629         
16630         if(this.multiple){
16631             this.syncValue();
16632             return;
16633         }
16634         
16635         var text = v;
16636         if(this.valueField){
16637             var r = this.findRecord(this.valueField, v);
16638             if(r){
16639                 text = r.data[this.displayField];
16640             }else if(this.valueNotFoundText !== undefined){
16641                 text = this.valueNotFoundText;
16642             }
16643         }
16644         this.lastSelectionText = text;
16645         if(this.hiddenField){
16646             this.hiddenField.dom.value = v;
16647         }
16648         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16649         this.value = v;
16650         
16651         var close = this.closeTriggerEl();
16652         
16653         if(close){
16654             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16655         }
16656         
16657         this.validate();
16658     },
16659     /**
16660      * @property {Object} the last set data for the element
16661      */
16662     
16663     lastData : false,
16664     /**
16665      * Sets the value of the field based on a object which is related to the record format for the store.
16666      * @param {Object} value the value to set as. or false on reset?
16667      */
16668     setFromData : function(o){
16669         
16670         if(this.multiple){
16671             this.addItem(o);
16672             return;
16673         }
16674             
16675         var dv = ''; // display value
16676         var vv = ''; // value value..
16677         this.lastData = o;
16678         if (this.displayField) {
16679             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16680         } else {
16681             // this is an error condition!!!
16682             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16683         }
16684         
16685         if(this.valueField){
16686             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16687         }
16688         
16689         var close = this.closeTriggerEl();
16690         
16691         if(close){
16692             if(dv.length || vv * 1 > 0){
16693                 close.show() ;
16694                 this.blockFocus=true;
16695             } else {
16696                 close.hide();
16697             }             
16698         }
16699         
16700         if(this.hiddenField){
16701             this.hiddenField.dom.value = vv;
16702             
16703             this.lastSelectionText = dv;
16704             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16705             this.value = vv;
16706             return;
16707         }
16708         // no hidden field.. - we store the value in 'value', but still display
16709         // display field!!!!
16710         this.lastSelectionText = dv;
16711         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16712         this.value = vv;
16713         
16714         
16715         
16716     },
16717     // private
16718     reset : function(){
16719         // overridden so that last data is reset..
16720         
16721         if(this.multiple){
16722             this.clearItem();
16723             return;
16724         }
16725         
16726         this.setValue(this.originalValue);
16727         //this.clearInvalid();
16728         this.lastData = false;
16729         if (this.view) {
16730             this.view.clearSelections();
16731         }
16732         
16733         this.validate();
16734     },
16735     // private
16736     findRecord : function(prop, value){
16737         var record;
16738         if(this.store.getCount() > 0){
16739             this.store.each(function(r){
16740                 if(r.data[prop] == value){
16741                     record = r;
16742                     return false;
16743                 }
16744                 return true;
16745             });
16746         }
16747         return record;
16748     },
16749     
16750     getName: function()
16751     {
16752         // returns hidden if it's set..
16753         if (!this.rendered) {return ''};
16754         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16755         
16756     },
16757     // private
16758     onViewMove : function(e, t){
16759         this.inKeyMode = false;
16760     },
16761
16762     // private
16763     onViewOver : function(e, t){
16764         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16765             return;
16766         }
16767         var item = this.view.findItemFromChild(t);
16768         
16769         if(item){
16770             var index = this.view.indexOf(item);
16771             this.select(index, false);
16772         }
16773     },
16774
16775     // private
16776     onViewClick : function(view, doFocus, el, e)
16777     {
16778         var index = this.view.getSelectedIndexes()[0];
16779         
16780         var r = this.store.getAt(index);
16781         
16782         if(this.tickable){
16783             
16784             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16785                 return;
16786             }
16787             
16788             var rm = false;
16789             var _this = this;
16790             
16791             Roo.each(this.tickItems, function(v,k){
16792                 
16793                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16794                     Roo.log(v);
16795                     _this.tickItems.splice(k, 1);
16796                     
16797                     if(typeof(e) == 'undefined' && view == false){
16798                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16799                     }
16800                     
16801                     rm = true;
16802                     return;
16803                 }
16804             });
16805             
16806             if(rm){
16807                 return;
16808             }
16809             
16810             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16811                 this.tickItems.push(r.data);
16812             }
16813             
16814             if(typeof(e) == 'undefined' && view == false){
16815                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16816             }
16817                     
16818             return;
16819         }
16820         
16821         if(r){
16822             this.onSelect(r, index);
16823         }
16824         if(doFocus !== false && !this.blockFocus){
16825             this.inputEl().focus();
16826         }
16827     },
16828
16829     // private
16830     restrictHeight : function(){
16831         //this.innerList.dom.style.height = '';
16832         //var inner = this.innerList.dom;
16833         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16834         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16835         //this.list.beginUpdate();
16836         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16837         this.list.alignTo(this.inputEl(), this.listAlign);
16838         this.list.alignTo(this.inputEl(), this.listAlign);
16839         //this.list.endUpdate();
16840     },
16841
16842     // private
16843     onEmptyResults : function(){
16844         
16845         if(this.tickable && this.editable){
16846             this.hasFocus = false;
16847             this.restrictHeight();
16848             return;
16849         }
16850         
16851         this.collapse();
16852     },
16853
16854     /**
16855      * Returns true if the dropdown list is expanded, else false.
16856      */
16857     isExpanded : function(){
16858         return this.list.isVisible();
16859     },
16860
16861     /**
16862      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16863      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16864      * @param {String} value The data value of the item to select
16865      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16866      * selected item if it is not currently in view (defaults to true)
16867      * @return {Boolean} True if the value matched an item in the list, else false
16868      */
16869     selectByValue : function(v, scrollIntoView){
16870         if(v !== undefined && v !== null){
16871             var r = this.findRecord(this.valueField || this.displayField, v);
16872             if(r){
16873                 this.select(this.store.indexOf(r), scrollIntoView);
16874                 return true;
16875             }
16876         }
16877         return false;
16878     },
16879
16880     /**
16881      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16882      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16883      * @param {Number} index The zero-based index of the list item to select
16884      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16885      * selected item if it is not currently in view (defaults to true)
16886      */
16887     select : function(index, scrollIntoView){
16888         this.selectedIndex = index;
16889         this.view.select(index);
16890         if(scrollIntoView !== false){
16891             var el = this.view.getNode(index);
16892             /*
16893              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16894              */
16895             if(el){
16896                 this.list.scrollChildIntoView(el, false);
16897             }
16898         }
16899     },
16900
16901     // private
16902     selectNext : function(){
16903         var ct = this.store.getCount();
16904         if(ct > 0){
16905             if(this.selectedIndex == -1){
16906                 this.select(0);
16907             }else if(this.selectedIndex < ct-1){
16908                 this.select(this.selectedIndex+1);
16909             }
16910         }
16911     },
16912
16913     // private
16914     selectPrev : function(){
16915         var ct = this.store.getCount();
16916         if(ct > 0){
16917             if(this.selectedIndex == -1){
16918                 this.select(0);
16919             }else if(this.selectedIndex != 0){
16920                 this.select(this.selectedIndex-1);
16921             }
16922         }
16923     },
16924
16925     // private
16926     onKeyUp : function(e){
16927         if(this.editable !== false && !e.isSpecialKey()){
16928             this.lastKey = e.getKey();
16929             this.dqTask.delay(this.queryDelay);
16930         }
16931     },
16932
16933     // private
16934     validateBlur : function(){
16935         return !this.list || !this.list.isVisible();   
16936     },
16937
16938     // private
16939     initQuery : function(){
16940         
16941         var v = this.getRawValue();
16942         
16943         if(this.tickable && this.editable){
16944             v = this.tickableInputEl().getValue();
16945         }
16946         
16947         this.doQuery(v);
16948     },
16949
16950     // private
16951     doForce : function(){
16952         if(this.inputEl().dom.value.length > 0){
16953             this.inputEl().dom.value =
16954                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16955              
16956         }
16957     },
16958
16959     /**
16960      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16961      * query allowing the query action to be canceled if needed.
16962      * @param {String} query The SQL query to execute
16963      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16964      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16965      * saved in the current store (defaults to false)
16966      */
16967     doQuery : function(q, forceAll){
16968         
16969         if(q === undefined || q === null){
16970             q = '';
16971         }
16972         var qe = {
16973             query: q,
16974             forceAll: forceAll,
16975             combo: this,
16976             cancel:false
16977         };
16978         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16979             return false;
16980         }
16981         q = qe.query;
16982         
16983         forceAll = qe.forceAll;
16984         if(forceAll === true || (q.length >= this.minChars)){
16985             
16986             this.hasQuery = true;
16987             
16988             if(this.lastQuery != q || this.alwaysQuery){
16989                 this.lastQuery = q;
16990                 if(this.mode == 'local'){
16991                     this.selectedIndex = -1;
16992                     if(forceAll){
16993                         this.store.clearFilter();
16994                     }else{
16995                         
16996                         if(this.specialFilter){
16997                             this.fireEvent('specialfilter', this);
16998                             this.onLoad();
16999                             return;
17000                         }
17001                         
17002                         this.store.filter(this.displayField, q);
17003                     }
17004                     
17005                     this.store.fireEvent("datachanged", this.store);
17006                     
17007                     this.onLoad();
17008                     
17009                     
17010                 }else{
17011                     
17012                     this.store.baseParams[this.queryParam] = q;
17013                     
17014                     var options = {params : this.getParams(q)};
17015                     
17016                     if(this.loadNext){
17017                         options.add = true;
17018                         options.params.start = this.page * this.pageSize;
17019                     }
17020                     
17021                     this.store.load(options);
17022                     
17023                     /*
17024                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17025                      *  we should expand the list on onLoad
17026                      *  so command out it
17027                      */
17028 //                    this.expand();
17029                 }
17030             }else{
17031                 this.selectedIndex = -1;
17032                 this.onLoad();   
17033             }
17034         }
17035         
17036         this.loadNext = false;
17037     },
17038     
17039     // private
17040     getParams : function(q){
17041         var p = {};
17042         //p[this.queryParam] = q;
17043         
17044         if(this.pageSize){
17045             p.start = 0;
17046             p.limit = this.pageSize;
17047         }
17048         return p;
17049     },
17050
17051     /**
17052      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17053      */
17054     collapse : function(){
17055         if(!this.isExpanded()){
17056             return;
17057         }
17058         
17059         this.list.hide();
17060         
17061         this.hasFocus = false;
17062         
17063         if(this.tickable){
17064             this.okBtn.hide();
17065             this.cancelBtn.hide();
17066             this.trigger.show();
17067             
17068             if(this.editable){
17069                 this.tickableInputEl().dom.value = '';
17070                 this.tickableInputEl().blur();
17071             }
17072             
17073         }
17074         
17075         Roo.get(document).un('mousedown', this.collapseIf, this);
17076         Roo.get(document).un('mousewheel', this.collapseIf, this);
17077         if (!this.editable) {
17078             Roo.get(document).un('keydown', this.listKeyPress, this);
17079         }
17080         this.fireEvent('collapse', this);
17081         
17082         this.validate();
17083     },
17084
17085     // private
17086     collapseIf : function(e){
17087         var in_combo  = e.within(this.el);
17088         var in_list =  e.within(this.list);
17089         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17090         
17091         if (in_combo || in_list || is_list) {
17092             //e.stopPropagation();
17093             return;
17094         }
17095         
17096         if(this.tickable){
17097             this.onTickableFooterButtonClick(e, false, false);
17098         }
17099
17100         this.collapse();
17101         
17102     },
17103
17104     /**
17105      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17106      */
17107     expand : function(){
17108        
17109         if(this.isExpanded() || !this.hasFocus){
17110             return;
17111         }
17112         
17113         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17114         this.list.setWidth(lw);
17115         
17116         Roo.log('expand');
17117         
17118         this.list.show();
17119         
17120         this.restrictHeight();
17121         
17122         if(this.tickable){
17123             
17124             this.tickItems = Roo.apply([], this.item);
17125             
17126             this.okBtn.show();
17127             this.cancelBtn.show();
17128             this.trigger.hide();
17129             
17130             if(this.editable){
17131                 this.tickableInputEl().focus();
17132             }
17133             
17134         }
17135         
17136         Roo.get(document).on('mousedown', this.collapseIf, this);
17137         Roo.get(document).on('mousewheel', this.collapseIf, this);
17138         if (!this.editable) {
17139             Roo.get(document).on('keydown', this.listKeyPress, this);
17140         }
17141         
17142         this.fireEvent('expand', this);
17143     },
17144
17145     // private
17146     // Implements the default empty TriggerField.onTriggerClick function
17147     onTriggerClick : function(e)
17148     {
17149         Roo.log('trigger click');
17150         
17151         if(this.disabled || !this.triggerList){
17152             return;
17153         }
17154         
17155         this.page = 0;
17156         this.loadNext = false;
17157         
17158         if(this.isExpanded()){
17159             this.collapse();
17160             if (!this.blockFocus) {
17161                 this.inputEl().focus();
17162             }
17163             
17164         }else {
17165             this.hasFocus = true;
17166             if(this.triggerAction == 'all') {
17167                 this.doQuery(this.allQuery, true);
17168             } else {
17169                 this.doQuery(this.getRawValue());
17170             }
17171             if (!this.blockFocus) {
17172                 this.inputEl().focus();
17173             }
17174         }
17175     },
17176     
17177     onTickableTriggerClick : function(e)
17178     {
17179         if(this.disabled){
17180             return;
17181         }
17182         
17183         this.page = 0;
17184         this.loadNext = false;
17185         this.hasFocus = true;
17186         
17187         if(this.triggerAction == 'all') {
17188             this.doQuery(this.allQuery, true);
17189         } else {
17190             this.doQuery(this.getRawValue());
17191         }
17192     },
17193     
17194     onSearchFieldClick : function(e)
17195     {
17196         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17197             this.onTickableFooterButtonClick(e, false, false);
17198             return;
17199         }
17200         
17201         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17202             return;
17203         }
17204         
17205         this.page = 0;
17206         this.loadNext = false;
17207         this.hasFocus = true;
17208         
17209         if(this.triggerAction == 'all') {
17210             this.doQuery(this.allQuery, true);
17211         } else {
17212             this.doQuery(this.getRawValue());
17213         }
17214     },
17215     
17216     listKeyPress : function(e)
17217     {
17218         //Roo.log('listkeypress');
17219         // scroll to first matching element based on key pres..
17220         if (e.isSpecialKey()) {
17221             return false;
17222         }
17223         var k = String.fromCharCode(e.getKey()).toUpperCase();
17224         //Roo.log(k);
17225         var match  = false;
17226         var csel = this.view.getSelectedNodes();
17227         var cselitem = false;
17228         if (csel.length) {
17229             var ix = this.view.indexOf(csel[0]);
17230             cselitem  = this.store.getAt(ix);
17231             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17232                 cselitem = false;
17233             }
17234             
17235         }
17236         
17237         this.store.each(function(v) { 
17238             if (cselitem) {
17239                 // start at existing selection.
17240                 if (cselitem.id == v.id) {
17241                     cselitem = false;
17242                 }
17243                 return true;
17244             }
17245                 
17246             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17247                 match = this.store.indexOf(v);
17248                 return false;
17249             }
17250             return true;
17251         }, this);
17252         
17253         if (match === false) {
17254             return true; // no more action?
17255         }
17256         // scroll to?
17257         this.view.select(match);
17258         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17259         sn.scrollIntoView(sn.dom.parentNode, false);
17260     },
17261     
17262     onViewScroll : function(e, t){
17263         
17264         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){
17265             return;
17266         }
17267         
17268         this.hasQuery = true;
17269         
17270         this.loading = this.list.select('.loading', true).first();
17271         
17272         if(this.loading === null){
17273             this.list.createChild({
17274                 tag: 'div',
17275                 cls: 'loading roo-select2-more-results roo-select2-active',
17276                 html: 'Loading more results...'
17277             });
17278             
17279             this.loading = this.list.select('.loading', true).first();
17280             
17281             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17282             
17283             this.loading.hide();
17284         }
17285         
17286         this.loading.show();
17287         
17288         var _combo = this;
17289         
17290         this.page++;
17291         this.loadNext = true;
17292         
17293         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17294         
17295         return;
17296     },
17297     
17298     addItem : function(o)
17299     {   
17300         var dv = ''; // display value
17301         
17302         if (this.displayField) {
17303             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17304         } else {
17305             // this is an error condition!!!
17306             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17307         }
17308         
17309         if(!dv.length){
17310             return;
17311         }
17312         
17313         var choice = this.choices.createChild({
17314             tag: 'li',
17315             cls: 'roo-select2-search-choice',
17316             cn: [
17317                 {
17318                     tag: 'div',
17319                     html: dv
17320                 },
17321                 {
17322                     tag: 'a',
17323                     href: '#',
17324                     cls: 'roo-select2-search-choice-close fa fa-times',
17325                     tabindex: '-1'
17326                 }
17327             ]
17328             
17329         }, this.searchField);
17330         
17331         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17332         
17333         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17334         
17335         this.item.push(o);
17336         
17337         this.lastData = o;
17338         
17339         this.syncValue();
17340         
17341         this.inputEl().dom.value = '';
17342         
17343         this.validate();
17344     },
17345     
17346     onRemoveItem : function(e, _self, o)
17347     {
17348         e.preventDefault();
17349         
17350         this.lastItem = Roo.apply([], this.item);
17351         
17352         var index = this.item.indexOf(o.data) * 1;
17353         
17354         if( index < 0){
17355             Roo.log('not this item?!');
17356             return;
17357         }
17358         
17359         this.item.splice(index, 1);
17360         o.item.remove();
17361         
17362         this.syncValue();
17363         
17364         this.fireEvent('remove', this, e);
17365         
17366         this.validate();
17367         
17368     },
17369     
17370     syncValue : function()
17371     {
17372         if(!this.item.length){
17373             this.clearValue();
17374             return;
17375         }
17376             
17377         var value = [];
17378         var _this = this;
17379         Roo.each(this.item, function(i){
17380             if(_this.valueField){
17381                 value.push(i[_this.valueField]);
17382                 return;
17383             }
17384
17385             value.push(i);
17386         });
17387
17388         this.value = value.join(',');
17389
17390         if(this.hiddenField){
17391             this.hiddenField.dom.value = this.value;
17392         }
17393         
17394         this.store.fireEvent("datachanged", this.store);
17395         
17396         this.validate();
17397     },
17398     
17399     clearItem : function()
17400     {
17401         if(!this.multiple){
17402             return;
17403         }
17404         
17405         this.item = [];
17406         
17407         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17408            c.remove();
17409         });
17410         
17411         this.syncValue();
17412         
17413         this.validate();
17414         
17415         if(this.tickable && !Roo.isTouch){
17416             this.view.refresh();
17417         }
17418     },
17419     
17420     inputEl: function ()
17421     {
17422         if(Roo.isIOS && this.useNativeIOS){
17423             return this.el.select('select.roo-ios-select', true).first();
17424         }
17425         
17426         if(Roo.isTouch && this.mobileTouchView){
17427             return this.el.select('input.form-control',true).first();
17428         }
17429         
17430         if(this.tickable){
17431             return this.searchField;
17432         }
17433         
17434         return this.el.select('input.form-control',true).first();
17435     },
17436     
17437     onTickableFooterButtonClick : function(e, btn, el)
17438     {
17439         e.preventDefault();
17440         
17441         this.lastItem = Roo.apply([], this.item);
17442         
17443         if(btn && btn.name == 'cancel'){
17444             this.tickItems = Roo.apply([], this.item);
17445             this.collapse();
17446             return;
17447         }
17448         
17449         this.clearItem();
17450         
17451         var _this = this;
17452         
17453         Roo.each(this.tickItems, function(o){
17454             _this.addItem(o);
17455         });
17456         
17457         this.collapse();
17458         
17459     },
17460     
17461     validate : function()
17462     {
17463         if(this.getVisibilityEl().hasClass('hidden')){
17464             return true;
17465         }
17466         
17467         var v = this.getRawValue();
17468         
17469         if(this.multiple){
17470             v = this.getValue();
17471         }
17472         
17473         if(this.disabled || this.allowBlank || v.length){
17474             this.markValid();
17475             return true;
17476         }
17477         
17478         this.markInvalid();
17479         return false;
17480     },
17481     
17482     tickableInputEl : function()
17483     {
17484         if(!this.tickable || !this.editable){
17485             return this.inputEl();
17486         }
17487         
17488         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17489     },
17490     
17491     
17492     getAutoCreateTouchView : function()
17493     {
17494         var id = Roo.id();
17495         
17496         var cfg = {
17497             cls: 'form-group' //input-group
17498         };
17499         
17500         var input =  {
17501             tag: 'input',
17502             id : id,
17503             type : this.inputType,
17504             cls : 'form-control x-combo-noedit',
17505             autocomplete: 'new-password',
17506             placeholder : this.placeholder || '',
17507             readonly : true
17508         };
17509         
17510         if (this.name) {
17511             input.name = this.name;
17512         }
17513         
17514         if (this.size) {
17515             input.cls += ' input-' + this.size;
17516         }
17517         
17518         if (this.disabled) {
17519             input.disabled = true;
17520         }
17521         
17522         var inputblock = {
17523             cls : 'roo-combobox-wrap',
17524             cn : [
17525                 input
17526             ]
17527         };
17528         
17529         if(this.before){
17530             inputblock.cls += ' input-group';
17531             
17532             inputblock.cn.unshift({
17533                 tag :'span',
17534                 cls : 'input-group-addon input-group-prepend input-group-text',
17535                 html : this.before
17536             });
17537         }
17538         
17539         if(this.removable && !this.multiple){
17540             inputblock.cls += ' roo-removable';
17541             
17542             inputblock.cn.push({
17543                 tag: 'button',
17544                 html : 'x',
17545                 cls : 'roo-combo-removable-btn close'
17546             });
17547         }
17548
17549         if(this.hasFeedback && !this.allowBlank){
17550             
17551             inputblock.cls += ' has-feedback';
17552             
17553             inputblock.cn.push({
17554                 tag: 'span',
17555                 cls: 'glyphicon form-control-feedback'
17556             });
17557             
17558         }
17559         
17560         if (this.after) {
17561             
17562             inputblock.cls += (this.before) ? '' : ' input-group';
17563             
17564             inputblock.cn.push({
17565                 tag :'span',
17566                 cls : 'input-group-addon input-group-append input-group-text',
17567                 html : this.after
17568             });
17569         }
17570
17571         
17572         var ibwrap = inputblock;
17573         
17574         if(this.multiple){
17575             ibwrap = {
17576                 tag: 'ul',
17577                 cls: 'roo-select2-choices',
17578                 cn:[
17579                     {
17580                         tag: 'li',
17581                         cls: 'roo-select2-search-field',
17582                         cn: [
17583
17584                             inputblock
17585                         ]
17586                     }
17587                 ]
17588             };
17589         
17590             
17591         }
17592         
17593         var combobox = {
17594             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17595             cn: [
17596                 {
17597                     tag: 'input',
17598                     type : 'hidden',
17599                     cls: 'form-hidden-field'
17600                 },
17601                 ibwrap
17602             ]
17603         };
17604         
17605         if(!this.multiple && this.showToggleBtn){
17606             
17607             var caret = {
17608                 cls: 'caret'
17609             };
17610             
17611             if (this.caret != false) {
17612                 caret = {
17613                      tag: 'i',
17614                      cls: 'fa fa-' + this.caret
17615                 };
17616                 
17617             }
17618             
17619             combobox.cn.push({
17620                 tag :'span',
17621                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17622                 cn : [
17623                     Roo.bootstrap.version == 3 ? caret : '',
17624                     {
17625                         tag: 'span',
17626                         cls: 'combobox-clear',
17627                         cn  : [
17628                             {
17629                                 tag : 'i',
17630                                 cls: 'icon-remove'
17631                             }
17632                         ]
17633                     }
17634                 ]
17635
17636             })
17637         }
17638         
17639         if(this.multiple){
17640             combobox.cls += ' roo-select2-container-multi';
17641         }
17642         
17643         var required =  this.allowBlank ?  {
17644                     tag : 'i',
17645                     style: 'display: none'
17646                 } : {
17647                    tag : 'i',
17648                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17649                    tooltip : 'This field is required'
17650                 };
17651         
17652         var align = this.labelAlign || this.parentLabelAlign();
17653         
17654         if (align ==='left' && this.fieldLabel.length) {
17655
17656             cfg.cn = [
17657                 required,
17658                 {
17659                     tag: 'label',
17660                     cls : 'control-label col-form-label',
17661                     html : this.fieldLabel
17662
17663                 },
17664                 {
17665                     cls : 'roo-combobox-wrap ', 
17666                     cn: [
17667                         combobox
17668                     ]
17669                 }
17670             ];
17671             
17672             var labelCfg = cfg.cn[1];
17673             var contentCfg = cfg.cn[2];
17674             
17675
17676             if(this.indicatorpos == 'right'){
17677                 cfg.cn = [
17678                     {
17679                         tag: 'label',
17680                         'for' :  id,
17681                         cls : 'control-label col-form-label',
17682                         cn : [
17683                             {
17684                                 tag : 'span',
17685                                 html : this.fieldLabel
17686                             },
17687                             required
17688                         ]
17689                     },
17690                     {
17691                         cls : "roo-combobox-wrap ",
17692                         cn: [
17693                             combobox
17694                         ]
17695                     }
17696
17697                 ];
17698                 
17699                 labelCfg = cfg.cn[0];
17700                 contentCfg = cfg.cn[1];
17701             }
17702             
17703            
17704             
17705             if(this.labelWidth > 12){
17706                 labelCfg.style = "width: " + this.labelWidth + 'px';
17707             }
17708            
17709             if(this.labelWidth < 13 && this.labelmd == 0){
17710                 this.labelmd = this.labelWidth;
17711             }
17712             
17713             if(this.labellg > 0){
17714                 labelCfg.cls += ' col-lg-' + this.labellg;
17715                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17716             }
17717             
17718             if(this.labelmd > 0){
17719                 labelCfg.cls += ' col-md-' + this.labelmd;
17720                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17721             }
17722             
17723             if(this.labelsm > 0){
17724                 labelCfg.cls += ' col-sm-' + this.labelsm;
17725                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17726             }
17727             
17728             if(this.labelxs > 0){
17729                 labelCfg.cls += ' col-xs-' + this.labelxs;
17730                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17731             }
17732                 
17733                 
17734         } else if ( this.fieldLabel.length) {
17735             cfg.cn = [
17736                required,
17737                 {
17738                     tag: 'label',
17739                     cls : 'control-label',
17740                     html : this.fieldLabel
17741
17742                 },
17743                 {
17744                     cls : '', 
17745                     cn: [
17746                         combobox
17747                     ]
17748                 }
17749             ];
17750             
17751             if(this.indicatorpos == 'right'){
17752                 cfg.cn = [
17753                     {
17754                         tag: 'label',
17755                         cls : 'control-label',
17756                         html : this.fieldLabel,
17757                         cn : [
17758                             required
17759                         ]
17760                     },
17761                     {
17762                         cls : '', 
17763                         cn: [
17764                             combobox
17765                         ]
17766                     }
17767                 ];
17768             }
17769         } else {
17770             cfg.cn = combobox;    
17771         }
17772         
17773         
17774         var settings = this;
17775         
17776         ['xs','sm','md','lg'].map(function(size){
17777             if (settings[size]) {
17778                 cfg.cls += ' col-' + size + '-' + settings[size];
17779             }
17780         });
17781         
17782         return cfg;
17783     },
17784     
17785     initTouchView : function()
17786     {
17787         this.renderTouchView();
17788         
17789         this.touchViewEl.on('scroll', function(){
17790             this.el.dom.scrollTop = 0;
17791         }, this);
17792         
17793         this.originalValue = this.getValue();
17794         
17795         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17796         
17797         this.inputEl().on("click", this.showTouchView, this);
17798         if (this.triggerEl) {
17799             this.triggerEl.on("click", this.showTouchView, this);
17800         }
17801         
17802         
17803         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17804         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17805         
17806         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17807         
17808         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17809         this.store.on('load', this.onTouchViewLoad, this);
17810         this.store.on('loadexception', this.onTouchViewLoadException, this);
17811         
17812         if(this.hiddenName){
17813             
17814             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17815             
17816             this.hiddenField.dom.value =
17817                 this.hiddenValue !== undefined ? this.hiddenValue :
17818                 this.value !== undefined ? this.value : '';
17819         
17820             this.el.dom.removeAttribute('name');
17821             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17822         }
17823         
17824         if(this.multiple){
17825             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17826             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17827         }
17828         
17829         if(this.removable && !this.multiple){
17830             var close = this.closeTriggerEl();
17831             if(close){
17832                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17833                 close.on('click', this.removeBtnClick, this, close);
17834             }
17835         }
17836         /*
17837          * fix the bug in Safari iOS8
17838          */
17839         this.inputEl().on("focus", function(e){
17840             document.activeElement.blur();
17841         }, this);
17842         
17843         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17844         
17845         return;
17846         
17847         
17848     },
17849     
17850     renderTouchView : function()
17851     {
17852         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17853         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17854         
17855         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17856         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17857         
17858         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17859         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17860         this.touchViewBodyEl.setStyle('overflow', 'auto');
17861         
17862         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17863         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17864         
17865         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17866         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17867         
17868     },
17869     
17870     showTouchView : function()
17871     {
17872         if(this.disabled){
17873             return;
17874         }
17875         
17876         this.touchViewHeaderEl.hide();
17877
17878         if(this.modalTitle.length){
17879             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17880             this.touchViewHeaderEl.show();
17881         }
17882
17883         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17884         this.touchViewEl.show();
17885
17886         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17887         
17888         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17889         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17890
17891         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17892
17893         if(this.modalTitle.length){
17894             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17895         }
17896         
17897         this.touchViewBodyEl.setHeight(bodyHeight);
17898
17899         if(this.animate){
17900             var _this = this;
17901             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17902         }else{
17903             this.touchViewEl.addClass(['in','show']);
17904         }
17905         
17906         if(this._touchViewMask){
17907             Roo.get(document.body).addClass("x-body-masked");
17908             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17909             this._touchViewMask.setStyle('z-index', 10000);
17910             this._touchViewMask.addClass('show');
17911         }
17912         
17913         this.doTouchViewQuery();
17914         
17915     },
17916     
17917     hideTouchView : function()
17918     {
17919         this.touchViewEl.removeClass(['in','show']);
17920
17921         if(this.animate){
17922             var _this = this;
17923             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17924         }else{
17925             this.touchViewEl.setStyle('display', 'none');
17926         }
17927         
17928         if(this._touchViewMask){
17929             this._touchViewMask.removeClass('show');
17930             Roo.get(document.body).removeClass("x-body-masked");
17931         }
17932     },
17933     
17934     setTouchViewValue : function()
17935     {
17936         if(this.multiple){
17937             this.clearItem();
17938         
17939             var _this = this;
17940
17941             Roo.each(this.tickItems, function(o){
17942                 this.addItem(o);
17943             }, this);
17944         }
17945         
17946         this.hideTouchView();
17947     },
17948     
17949     doTouchViewQuery : function()
17950     {
17951         var qe = {
17952             query: '',
17953             forceAll: true,
17954             combo: this,
17955             cancel:false
17956         };
17957         
17958         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17959             return false;
17960         }
17961         
17962         if(!this.alwaysQuery || this.mode == 'local'){
17963             this.onTouchViewLoad();
17964             return;
17965         }
17966         
17967         this.store.load();
17968     },
17969     
17970     onTouchViewBeforeLoad : function(combo,opts)
17971     {
17972         return;
17973     },
17974
17975     // private
17976     onTouchViewLoad : function()
17977     {
17978         if(this.store.getCount() < 1){
17979             this.onTouchViewEmptyResults();
17980             return;
17981         }
17982         
17983         this.clearTouchView();
17984         
17985         var rawValue = this.getRawValue();
17986         
17987         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17988         
17989         this.tickItems = [];
17990         
17991         this.store.data.each(function(d, rowIndex){
17992             var row = this.touchViewListGroup.createChild(template);
17993             
17994             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17995                 row.addClass(d.data.cls);
17996             }
17997             
17998             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17999                 var cfg = {
18000                     data : d.data,
18001                     html : d.data[this.displayField]
18002                 };
18003                 
18004                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18005                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18006                 }
18007             }
18008             row.removeClass('selected');
18009             if(!this.multiple && this.valueField &&
18010                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18011             {
18012                 // radio buttons..
18013                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18014                 row.addClass('selected');
18015             }
18016             
18017             if(this.multiple && this.valueField &&
18018                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18019             {
18020                 
18021                 // checkboxes...
18022                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18023                 this.tickItems.push(d.data);
18024             }
18025             
18026             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18027             
18028         }, this);
18029         
18030         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18031         
18032         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18033
18034         if(this.modalTitle.length){
18035             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18036         }
18037
18038         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18039         
18040         if(this.mobile_restrict_height && listHeight < bodyHeight){
18041             this.touchViewBodyEl.setHeight(listHeight);
18042         }
18043         
18044         var _this = this;
18045         
18046         if(firstChecked && listHeight > bodyHeight){
18047             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18048         }
18049         
18050     },
18051     
18052     onTouchViewLoadException : function()
18053     {
18054         this.hideTouchView();
18055     },
18056     
18057     onTouchViewEmptyResults : function()
18058     {
18059         this.clearTouchView();
18060         
18061         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18062         
18063         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18064         
18065     },
18066     
18067     clearTouchView : function()
18068     {
18069         this.touchViewListGroup.dom.innerHTML = '';
18070     },
18071     
18072     onTouchViewClick : function(e, el, o)
18073     {
18074         e.preventDefault();
18075         
18076         var row = o.row;
18077         var rowIndex = o.rowIndex;
18078         
18079         var r = this.store.getAt(rowIndex);
18080         
18081         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18082             
18083             if(!this.multiple){
18084                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18085                     c.dom.removeAttribute('checked');
18086                 }, this);
18087
18088                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18089
18090                 this.setFromData(r.data);
18091
18092                 var close = this.closeTriggerEl();
18093
18094                 if(close){
18095                     close.show();
18096                 }
18097
18098                 this.hideTouchView();
18099
18100                 this.fireEvent('select', this, r, rowIndex);
18101
18102                 return;
18103             }
18104
18105             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18106                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18107                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18108                 return;
18109             }
18110
18111             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18112             this.addItem(r.data);
18113             this.tickItems.push(r.data);
18114         }
18115     },
18116     
18117     getAutoCreateNativeIOS : function()
18118     {
18119         var cfg = {
18120             cls: 'form-group' //input-group,
18121         };
18122         
18123         var combobox =  {
18124             tag: 'select',
18125             cls : 'roo-ios-select'
18126         };
18127         
18128         if (this.name) {
18129             combobox.name = this.name;
18130         }
18131         
18132         if (this.disabled) {
18133             combobox.disabled = true;
18134         }
18135         
18136         var settings = this;
18137         
18138         ['xs','sm','md','lg'].map(function(size){
18139             if (settings[size]) {
18140                 cfg.cls += ' col-' + size + '-' + settings[size];
18141             }
18142         });
18143         
18144         cfg.cn = combobox;
18145         
18146         return cfg;
18147         
18148     },
18149     
18150     initIOSView : function()
18151     {
18152         this.store.on('load', this.onIOSViewLoad, this);
18153         
18154         return;
18155     },
18156     
18157     onIOSViewLoad : function()
18158     {
18159         if(this.store.getCount() < 1){
18160             return;
18161         }
18162         
18163         this.clearIOSView();
18164         
18165         if(this.allowBlank) {
18166             
18167             var default_text = '-- SELECT --';
18168             
18169             if(this.placeholder.length){
18170                 default_text = this.placeholder;
18171             }
18172             
18173             if(this.emptyTitle.length){
18174                 default_text += ' - ' + this.emptyTitle + ' -';
18175             }
18176             
18177             var opt = this.inputEl().createChild({
18178                 tag: 'option',
18179                 value : 0,
18180                 html : default_text
18181             });
18182             
18183             var o = {};
18184             o[this.valueField] = 0;
18185             o[this.displayField] = default_text;
18186             
18187             this.ios_options.push({
18188                 data : o,
18189                 el : opt
18190             });
18191             
18192         }
18193         
18194         this.store.data.each(function(d, rowIndex){
18195             
18196             var html = '';
18197             
18198             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18199                 html = d.data[this.displayField];
18200             }
18201             
18202             var value = '';
18203             
18204             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18205                 value = d.data[this.valueField];
18206             }
18207             
18208             var option = {
18209                 tag: 'option',
18210                 value : value,
18211                 html : html
18212             };
18213             
18214             if(this.value == d.data[this.valueField]){
18215                 option['selected'] = true;
18216             }
18217             
18218             var opt = this.inputEl().createChild(option);
18219             
18220             this.ios_options.push({
18221                 data : d.data,
18222                 el : opt
18223             });
18224             
18225         }, this);
18226         
18227         this.inputEl().on('change', function(){
18228            this.fireEvent('select', this);
18229         }, this);
18230         
18231     },
18232     
18233     clearIOSView: function()
18234     {
18235         this.inputEl().dom.innerHTML = '';
18236         
18237         this.ios_options = [];
18238     },
18239     
18240     setIOSValue: function(v)
18241     {
18242         this.value = v;
18243         
18244         if(!this.ios_options){
18245             return;
18246         }
18247         
18248         Roo.each(this.ios_options, function(opts){
18249            
18250            opts.el.dom.removeAttribute('selected');
18251            
18252            if(opts.data[this.valueField] != v){
18253                return;
18254            }
18255            
18256            opts.el.dom.setAttribute('selected', true);
18257            
18258         }, this);
18259     }
18260
18261     /** 
18262     * @cfg {Boolean} grow 
18263     * @hide 
18264     */
18265     /** 
18266     * @cfg {Number} growMin 
18267     * @hide 
18268     */
18269     /** 
18270     * @cfg {Number} growMax 
18271     * @hide 
18272     */
18273     /**
18274      * @hide
18275      * @method autoSize
18276      */
18277 });
18278
18279 Roo.apply(Roo.bootstrap.ComboBox,  {
18280     
18281     header : {
18282         tag: 'div',
18283         cls: 'modal-header',
18284         cn: [
18285             {
18286                 tag: 'h4',
18287                 cls: 'modal-title'
18288             }
18289         ]
18290     },
18291     
18292     body : {
18293         tag: 'div',
18294         cls: 'modal-body',
18295         cn: [
18296             {
18297                 tag: 'ul',
18298                 cls: 'list-group'
18299             }
18300         ]
18301     },
18302     
18303     listItemRadio : {
18304         tag: 'li',
18305         cls: 'list-group-item',
18306         cn: [
18307             {
18308                 tag: 'span',
18309                 cls: 'roo-combobox-list-group-item-value'
18310             },
18311             {
18312                 tag: 'div',
18313                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18314                 cn: [
18315                     {
18316                         tag: 'input',
18317                         type: 'radio'
18318                     },
18319                     {
18320                         tag: 'label'
18321                     }
18322                 ]
18323             }
18324         ]
18325     },
18326     
18327     listItemCheckbox : {
18328         tag: 'li',
18329         cls: 'list-group-item',
18330         cn: [
18331             {
18332                 tag: 'span',
18333                 cls: 'roo-combobox-list-group-item-value'
18334             },
18335             {
18336                 tag: 'div',
18337                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18338                 cn: [
18339                     {
18340                         tag: 'input',
18341                         type: 'checkbox'
18342                     },
18343                     {
18344                         tag: 'label'
18345                     }
18346                 ]
18347             }
18348         ]
18349     },
18350     
18351     emptyResult : {
18352         tag: 'div',
18353         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18354     },
18355     
18356     footer : {
18357         tag: 'div',
18358         cls: 'modal-footer',
18359         cn: [
18360             {
18361                 tag: 'div',
18362                 cls: 'row',
18363                 cn: [
18364                     {
18365                         tag: 'div',
18366                         cls: 'col-xs-6 text-left',
18367                         cn: {
18368                             tag: 'button',
18369                             cls: 'btn btn-danger roo-touch-view-cancel',
18370                             html: 'Cancel'
18371                         }
18372                     },
18373                     {
18374                         tag: 'div',
18375                         cls: 'col-xs-6 text-right',
18376                         cn: {
18377                             tag: 'button',
18378                             cls: 'btn btn-success roo-touch-view-ok',
18379                             html: 'OK'
18380                         }
18381                     }
18382                 ]
18383             }
18384         ]
18385         
18386     }
18387 });
18388
18389 Roo.apply(Roo.bootstrap.ComboBox,  {
18390     
18391     touchViewTemplate : {
18392         tag: 'div',
18393         cls: 'modal fade roo-combobox-touch-view',
18394         cn: [
18395             {
18396                 tag: 'div',
18397                 cls: 'modal-dialog',
18398                 style : 'position:fixed', // we have to fix position....
18399                 cn: [
18400                     {
18401                         tag: 'div',
18402                         cls: 'modal-content',
18403                         cn: [
18404                             Roo.bootstrap.ComboBox.header,
18405                             Roo.bootstrap.ComboBox.body,
18406                             Roo.bootstrap.ComboBox.footer
18407                         ]
18408                     }
18409                 ]
18410             }
18411         ]
18412     }
18413 });/*
18414  * Based on:
18415  * Ext JS Library 1.1.1
18416  * Copyright(c) 2006-2007, Ext JS, LLC.
18417  *
18418  * Originally Released Under LGPL - original licence link has changed is not relivant.
18419  *
18420  * Fork - LGPL
18421  * <script type="text/javascript">
18422  */
18423
18424 /**
18425  * @class Roo.View
18426  * @extends Roo.util.Observable
18427  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18428  * This class also supports single and multi selection modes. <br>
18429  * Create a data model bound view:
18430  <pre><code>
18431  var store = new Roo.data.Store(...);
18432
18433  var view = new Roo.View({
18434     el : "my-element",
18435     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18436  
18437     singleSelect: true,
18438     selectedClass: "ydataview-selected",
18439     store: store
18440  });
18441
18442  // listen for node click?
18443  view.on("click", function(vw, index, node, e){
18444  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18445  });
18446
18447  // load XML data
18448  dataModel.load("foobar.xml");
18449  </code></pre>
18450  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18451  * <br><br>
18452  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18453  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18454  * 
18455  * Note: old style constructor is still suported (container, template, config)
18456  * 
18457  * @constructor
18458  * Create a new View
18459  * @param {Object} config The config object
18460  * 
18461  */
18462 Roo.View = function(config, depreciated_tpl, depreciated_config){
18463     
18464     this.parent = false;
18465     
18466     if (typeof(depreciated_tpl) == 'undefined') {
18467         // new way.. - universal constructor.
18468         Roo.apply(this, config);
18469         this.el  = Roo.get(this.el);
18470     } else {
18471         // old format..
18472         this.el  = Roo.get(config);
18473         this.tpl = depreciated_tpl;
18474         Roo.apply(this, depreciated_config);
18475     }
18476     this.wrapEl  = this.el.wrap().wrap();
18477     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18478     
18479     
18480     if(typeof(this.tpl) == "string"){
18481         this.tpl = new Roo.Template(this.tpl);
18482     } else {
18483         // support xtype ctors..
18484         this.tpl = new Roo.factory(this.tpl, Roo);
18485     }
18486     
18487     
18488     this.tpl.compile();
18489     
18490     /** @private */
18491     this.addEvents({
18492         /**
18493          * @event beforeclick
18494          * Fires before a click is processed. Returns false to cancel the default action.
18495          * @param {Roo.View} this
18496          * @param {Number} index The index of the target node
18497          * @param {HTMLElement} node The target node
18498          * @param {Roo.EventObject} e The raw event object
18499          */
18500             "beforeclick" : true,
18501         /**
18502          * @event click
18503          * Fires when a template node is clicked.
18504          * @param {Roo.View} this
18505          * @param {Number} index The index of the target node
18506          * @param {HTMLElement} node The target node
18507          * @param {Roo.EventObject} e The raw event object
18508          */
18509             "click" : true,
18510         /**
18511          * @event dblclick
18512          * Fires when a template node is double clicked.
18513          * @param {Roo.View} this
18514          * @param {Number} index The index of the target node
18515          * @param {HTMLElement} node The target node
18516          * @param {Roo.EventObject} e The raw event object
18517          */
18518             "dblclick" : true,
18519         /**
18520          * @event contextmenu
18521          * Fires when a template node is right clicked.
18522          * @param {Roo.View} this
18523          * @param {Number} index The index of the target node
18524          * @param {HTMLElement} node The target node
18525          * @param {Roo.EventObject} e The raw event object
18526          */
18527             "contextmenu" : true,
18528         /**
18529          * @event selectionchange
18530          * Fires when the selected nodes change.
18531          * @param {Roo.View} this
18532          * @param {Array} selections Array of the selected nodes
18533          */
18534             "selectionchange" : true,
18535     
18536         /**
18537          * @event beforeselect
18538          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18539          * @param {Roo.View} this
18540          * @param {HTMLElement} node The node to be selected
18541          * @param {Array} selections Array of currently selected nodes
18542          */
18543             "beforeselect" : true,
18544         /**
18545          * @event preparedata
18546          * Fires on every row to render, to allow you to change the data.
18547          * @param {Roo.View} this
18548          * @param {Object} data to be rendered (change this)
18549          */
18550           "preparedata" : true
18551           
18552           
18553         });
18554
18555
18556
18557     this.el.on({
18558         "click": this.onClick,
18559         "dblclick": this.onDblClick,
18560         "contextmenu": this.onContextMenu,
18561         scope:this
18562     });
18563
18564     this.selections = [];
18565     this.nodes = [];
18566     this.cmp = new Roo.CompositeElementLite([]);
18567     if(this.store){
18568         this.store = Roo.factory(this.store, Roo.data);
18569         this.setStore(this.store, true);
18570     }
18571     
18572     if ( this.footer && this.footer.xtype) {
18573            
18574          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18575         
18576         this.footer.dataSource = this.store;
18577         this.footer.container = fctr;
18578         this.footer = Roo.factory(this.footer, Roo);
18579         fctr.insertFirst(this.el);
18580         
18581         // this is a bit insane - as the paging toolbar seems to detach the el..
18582 //        dom.parentNode.parentNode.parentNode
18583          // they get detached?
18584     }
18585     
18586     
18587     Roo.View.superclass.constructor.call(this);
18588     
18589     
18590 };
18591
18592 Roo.extend(Roo.View, Roo.util.Observable, {
18593     
18594      /**
18595      * @cfg {Roo.data.Store} store Data store to load data from.
18596      */
18597     store : false,
18598     
18599     /**
18600      * @cfg {String|Roo.Element} el The container element.
18601      */
18602     el : '',
18603     
18604     /**
18605      * @cfg {String|Roo.Template} tpl The template used by this View 
18606      */
18607     tpl : false,
18608     /**
18609      * @cfg {String} dataName the named area of the template to use as the data area
18610      *                          Works with domtemplates roo-name="name"
18611      */
18612     dataName: false,
18613     /**
18614      * @cfg {String} selectedClass The css class to add to selected nodes
18615      */
18616     selectedClass : "x-view-selected",
18617      /**
18618      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18619      */
18620     emptyText : "",
18621     
18622     /**
18623      * @cfg {String} text to display on mask (default Loading)
18624      */
18625     mask : false,
18626     /**
18627      * @cfg {Boolean} multiSelect Allow multiple selection
18628      */
18629     multiSelect : false,
18630     /**
18631      * @cfg {Boolean} singleSelect Allow single selection
18632      */
18633     singleSelect:  false,
18634     
18635     /**
18636      * @cfg {Boolean} toggleSelect - selecting 
18637      */
18638     toggleSelect : false,
18639     
18640     /**
18641      * @cfg {Boolean} tickable - selecting 
18642      */
18643     tickable : false,
18644     
18645     /**
18646      * Returns the element this view is bound to.
18647      * @return {Roo.Element}
18648      */
18649     getEl : function(){
18650         return this.wrapEl;
18651     },
18652     
18653     
18654
18655     /**
18656      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18657      */
18658     refresh : function(){
18659         //Roo.log('refresh');
18660         var t = this.tpl;
18661         
18662         // if we are using something like 'domtemplate', then
18663         // the what gets used is:
18664         // t.applySubtemplate(NAME, data, wrapping data..)
18665         // the outer template then get' applied with
18666         //     the store 'extra data'
18667         // and the body get's added to the
18668         //      roo-name="data" node?
18669         //      <span class='roo-tpl-{name}'></span> ?????
18670         
18671         
18672         
18673         this.clearSelections();
18674         this.el.update("");
18675         var html = [];
18676         var records = this.store.getRange();
18677         if(records.length < 1) {
18678             
18679             // is this valid??  = should it render a template??
18680             
18681             this.el.update(this.emptyText);
18682             return;
18683         }
18684         var el = this.el;
18685         if (this.dataName) {
18686             this.el.update(t.apply(this.store.meta)); //????
18687             el = this.el.child('.roo-tpl-' + this.dataName);
18688         }
18689         
18690         for(var i = 0, len = records.length; i < len; i++){
18691             var data = this.prepareData(records[i].data, i, records[i]);
18692             this.fireEvent("preparedata", this, data, i, records[i]);
18693             
18694             var d = Roo.apply({}, data);
18695             
18696             if(this.tickable){
18697                 Roo.apply(d, {'roo-id' : Roo.id()});
18698                 
18699                 var _this = this;
18700             
18701                 Roo.each(this.parent.item, function(item){
18702                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18703                         return;
18704                     }
18705                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18706                 });
18707             }
18708             
18709             html[html.length] = Roo.util.Format.trim(
18710                 this.dataName ?
18711                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18712                     t.apply(d)
18713             );
18714         }
18715         
18716         
18717         
18718         el.update(html.join(""));
18719         this.nodes = el.dom.childNodes;
18720         this.updateIndexes(0);
18721     },
18722     
18723
18724     /**
18725      * Function to override to reformat the data that is sent to
18726      * the template for each node.
18727      * DEPRICATED - use the preparedata event handler.
18728      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18729      * a JSON object for an UpdateManager bound view).
18730      */
18731     prepareData : function(data, index, record)
18732     {
18733         this.fireEvent("preparedata", this, data, index, record);
18734         return data;
18735     },
18736
18737     onUpdate : function(ds, record){
18738         // Roo.log('on update');   
18739         this.clearSelections();
18740         var index = this.store.indexOf(record);
18741         var n = this.nodes[index];
18742         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18743         n.parentNode.removeChild(n);
18744         this.updateIndexes(index, index);
18745     },
18746
18747     
18748     
18749 // --------- FIXME     
18750     onAdd : function(ds, records, index)
18751     {
18752         //Roo.log(['on Add', ds, records, index] );        
18753         this.clearSelections();
18754         if(this.nodes.length == 0){
18755             this.refresh();
18756             return;
18757         }
18758         var n = this.nodes[index];
18759         for(var i = 0, len = records.length; i < len; i++){
18760             var d = this.prepareData(records[i].data, i, records[i]);
18761             if(n){
18762                 this.tpl.insertBefore(n, d);
18763             }else{
18764                 
18765                 this.tpl.append(this.el, d);
18766             }
18767         }
18768         this.updateIndexes(index);
18769     },
18770
18771     onRemove : function(ds, record, index){
18772        // Roo.log('onRemove');
18773         this.clearSelections();
18774         var el = this.dataName  ?
18775             this.el.child('.roo-tpl-' + this.dataName) :
18776             this.el; 
18777         
18778         el.dom.removeChild(this.nodes[index]);
18779         this.updateIndexes(index);
18780     },
18781
18782     /**
18783      * Refresh an individual node.
18784      * @param {Number} index
18785      */
18786     refreshNode : function(index){
18787         this.onUpdate(this.store, this.store.getAt(index));
18788     },
18789
18790     updateIndexes : function(startIndex, endIndex){
18791         var ns = this.nodes;
18792         startIndex = startIndex || 0;
18793         endIndex = endIndex || ns.length - 1;
18794         for(var i = startIndex; i <= endIndex; i++){
18795             ns[i].nodeIndex = i;
18796         }
18797     },
18798
18799     /**
18800      * Changes the data store this view uses and refresh the view.
18801      * @param {Store} store
18802      */
18803     setStore : function(store, initial){
18804         if(!initial && this.store){
18805             this.store.un("datachanged", this.refresh);
18806             this.store.un("add", this.onAdd);
18807             this.store.un("remove", this.onRemove);
18808             this.store.un("update", this.onUpdate);
18809             this.store.un("clear", this.refresh);
18810             this.store.un("beforeload", this.onBeforeLoad);
18811             this.store.un("load", this.onLoad);
18812             this.store.un("loadexception", this.onLoad);
18813         }
18814         if(store){
18815           
18816             store.on("datachanged", this.refresh, this);
18817             store.on("add", this.onAdd, this);
18818             store.on("remove", this.onRemove, this);
18819             store.on("update", this.onUpdate, this);
18820             store.on("clear", this.refresh, this);
18821             store.on("beforeload", this.onBeforeLoad, this);
18822             store.on("load", this.onLoad, this);
18823             store.on("loadexception", this.onLoad, this);
18824         }
18825         
18826         if(store){
18827             this.refresh();
18828         }
18829     },
18830     /**
18831      * onbeforeLoad - masks the loading area.
18832      *
18833      */
18834     onBeforeLoad : function(store,opts)
18835     {
18836          //Roo.log('onBeforeLoad');   
18837         if (!opts.add) {
18838             this.el.update("");
18839         }
18840         this.el.mask(this.mask ? this.mask : "Loading" ); 
18841     },
18842     onLoad : function ()
18843     {
18844         this.el.unmask();
18845     },
18846     
18847
18848     /**
18849      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18850      * @param {HTMLElement} node
18851      * @return {HTMLElement} The template node
18852      */
18853     findItemFromChild : function(node){
18854         var el = this.dataName  ?
18855             this.el.child('.roo-tpl-' + this.dataName,true) :
18856             this.el.dom; 
18857         
18858         if(!node || node.parentNode == el){
18859                     return node;
18860             }
18861             var p = node.parentNode;
18862             while(p && p != el){
18863             if(p.parentNode == el){
18864                 return p;
18865             }
18866             p = p.parentNode;
18867         }
18868             return null;
18869     },
18870
18871     /** @ignore */
18872     onClick : function(e){
18873         var item = this.findItemFromChild(e.getTarget());
18874         if(item){
18875             var index = this.indexOf(item);
18876             if(this.onItemClick(item, index, e) !== false){
18877                 this.fireEvent("click", this, index, item, e);
18878             }
18879         }else{
18880             this.clearSelections();
18881         }
18882     },
18883
18884     /** @ignore */
18885     onContextMenu : function(e){
18886         var item = this.findItemFromChild(e.getTarget());
18887         if(item){
18888             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18889         }
18890     },
18891
18892     /** @ignore */
18893     onDblClick : function(e){
18894         var item = this.findItemFromChild(e.getTarget());
18895         if(item){
18896             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18897         }
18898     },
18899
18900     onItemClick : function(item, index, e)
18901     {
18902         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18903             return false;
18904         }
18905         if (this.toggleSelect) {
18906             var m = this.isSelected(item) ? 'unselect' : 'select';
18907             //Roo.log(m);
18908             var _t = this;
18909             _t[m](item, true, false);
18910             return true;
18911         }
18912         if(this.multiSelect || this.singleSelect){
18913             if(this.multiSelect && e.shiftKey && this.lastSelection){
18914                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18915             }else{
18916                 this.select(item, this.multiSelect && e.ctrlKey);
18917                 this.lastSelection = item;
18918             }
18919             
18920             if(!this.tickable){
18921                 e.preventDefault();
18922             }
18923             
18924         }
18925         return true;
18926     },
18927
18928     /**
18929      * Get the number of selected nodes.
18930      * @return {Number}
18931      */
18932     getSelectionCount : function(){
18933         return this.selections.length;
18934     },
18935
18936     /**
18937      * Get the currently selected nodes.
18938      * @return {Array} An array of HTMLElements
18939      */
18940     getSelectedNodes : function(){
18941         return this.selections;
18942     },
18943
18944     /**
18945      * Get the indexes of the selected nodes.
18946      * @return {Array}
18947      */
18948     getSelectedIndexes : function(){
18949         var indexes = [], s = this.selections;
18950         for(var i = 0, len = s.length; i < len; i++){
18951             indexes.push(s[i].nodeIndex);
18952         }
18953         return indexes;
18954     },
18955
18956     /**
18957      * Clear all selections
18958      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18959      */
18960     clearSelections : function(suppressEvent){
18961         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18962             this.cmp.elements = this.selections;
18963             this.cmp.removeClass(this.selectedClass);
18964             this.selections = [];
18965             if(!suppressEvent){
18966                 this.fireEvent("selectionchange", this, this.selections);
18967             }
18968         }
18969     },
18970
18971     /**
18972      * Returns true if the passed node is selected
18973      * @param {HTMLElement/Number} node The node or node index
18974      * @return {Boolean}
18975      */
18976     isSelected : function(node){
18977         var s = this.selections;
18978         if(s.length < 1){
18979             return false;
18980         }
18981         node = this.getNode(node);
18982         return s.indexOf(node) !== -1;
18983     },
18984
18985     /**
18986      * Selects nodes.
18987      * @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
18988      * @param {Boolean} keepExisting (optional) true to keep existing selections
18989      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18990      */
18991     select : function(nodeInfo, keepExisting, suppressEvent){
18992         if(nodeInfo instanceof Array){
18993             if(!keepExisting){
18994                 this.clearSelections(true);
18995             }
18996             for(var i = 0, len = nodeInfo.length; i < len; i++){
18997                 this.select(nodeInfo[i], true, true);
18998             }
18999             return;
19000         } 
19001         var node = this.getNode(nodeInfo);
19002         if(!node || this.isSelected(node)){
19003             return; // already selected.
19004         }
19005         if(!keepExisting){
19006             this.clearSelections(true);
19007         }
19008         
19009         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19010             Roo.fly(node).addClass(this.selectedClass);
19011             this.selections.push(node);
19012             if(!suppressEvent){
19013                 this.fireEvent("selectionchange", this, this.selections);
19014             }
19015         }
19016         
19017         
19018     },
19019       /**
19020      * Unselects nodes.
19021      * @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
19022      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19023      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19024      */
19025     unselect : function(nodeInfo, keepExisting, suppressEvent)
19026     {
19027         if(nodeInfo instanceof Array){
19028             Roo.each(this.selections, function(s) {
19029                 this.unselect(s, nodeInfo);
19030             }, this);
19031             return;
19032         }
19033         var node = this.getNode(nodeInfo);
19034         if(!node || !this.isSelected(node)){
19035             //Roo.log("not selected");
19036             return; // not selected.
19037         }
19038         // fireevent???
19039         var ns = [];
19040         Roo.each(this.selections, function(s) {
19041             if (s == node ) {
19042                 Roo.fly(node).removeClass(this.selectedClass);
19043
19044                 return;
19045             }
19046             ns.push(s);
19047         },this);
19048         
19049         this.selections= ns;
19050         this.fireEvent("selectionchange", this, this.selections);
19051     },
19052
19053     /**
19054      * Gets a template node.
19055      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19056      * @return {HTMLElement} The node or null if it wasn't found
19057      */
19058     getNode : function(nodeInfo){
19059         if(typeof nodeInfo == "string"){
19060             return document.getElementById(nodeInfo);
19061         }else if(typeof nodeInfo == "number"){
19062             return this.nodes[nodeInfo];
19063         }
19064         return nodeInfo;
19065     },
19066
19067     /**
19068      * Gets a range template nodes.
19069      * @param {Number} startIndex
19070      * @param {Number} endIndex
19071      * @return {Array} An array of nodes
19072      */
19073     getNodes : function(start, end){
19074         var ns = this.nodes;
19075         start = start || 0;
19076         end = typeof end == "undefined" ? ns.length - 1 : end;
19077         var nodes = [];
19078         if(start <= end){
19079             for(var i = start; i <= end; i++){
19080                 nodes.push(ns[i]);
19081             }
19082         } else{
19083             for(var i = start; i >= end; i--){
19084                 nodes.push(ns[i]);
19085             }
19086         }
19087         return nodes;
19088     },
19089
19090     /**
19091      * Finds the index of the passed node
19092      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19093      * @return {Number} The index of the node or -1
19094      */
19095     indexOf : function(node){
19096         node = this.getNode(node);
19097         if(typeof node.nodeIndex == "number"){
19098             return node.nodeIndex;
19099         }
19100         var ns = this.nodes;
19101         for(var i = 0, len = ns.length; i < len; i++){
19102             if(ns[i] == node){
19103                 return i;
19104             }
19105         }
19106         return -1;
19107     }
19108 });
19109 /*
19110  * - LGPL
19111  *
19112  * based on jquery fullcalendar
19113  * 
19114  */
19115
19116 Roo.bootstrap = Roo.bootstrap || {};
19117 /**
19118  * @class Roo.bootstrap.Calendar
19119  * @extends Roo.bootstrap.Component
19120  * Bootstrap Calendar class
19121  * @cfg {Boolean} loadMask (true|false) default false
19122  * @cfg {Object} header generate the user specific header of the calendar, default false
19123
19124  * @constructor
19125  * Create a new Container
19126  * @param {Object} config The config object
19127  */
19128
19129
19130
19131 Roo.bootstrap.Calendar = function(config){
19132     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19133      this.addEvents({
19134         /**
19135              * @event select
19136              * Fires when a date is selected
19137              * @param {DatePicker} this
19138              * @param {Date} date The selected date
19139              */
19140         'select': true,
19141         /**
19142              * @event monthchange
19143              * Fires when the displayed month changes 
19144              * @param {DatePicker} this
19145              * @param {Date} date The selected month
19146              */
19147         'monthchange': true,
19148         /**
19149              * @event evententer
19150              * Fires when mouse over an event
19151              * @param {Calendar} this
19152              * @param {event} Event
19153              */
19154         'evententer': true,
19155         /**
19156              * @event eventleave
19157              * Fires when the mouse leaves an
19158              * @param {Calendar} this
19159              * @param {event}
19160              */
19161         'eventleave': true,
19162         /**
19163              * @event eventclick
19164              * Fires when the mouse click an
19165              * @param {Calendar} this
19166              * @param {event}
19167              */
19168         'eventclick': true
19169         
19170     });
19171
19172 };
19173
19174 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19175     
19176      /**
19177      * @cfg {Number} startDay
19178      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19179      */
19180     startDay : 0,
19181     
19182     loadMask : false,
19183     
19184     header : false,
19185       
19186     getAutoCreate : function(){
19187         
19188         
19189         var fc_button = function(name, corner, style, content ) {
19190             return Roo.apply({},{
19191                 tag : 'span',
19192                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19193                          (corner.length ?
19194                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19195                             ''
19196                         ),
19197                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19198                 unselectable: 'on'
19199             });
19200         };
19201         
19202         var header = {};
19203         
19204         if(!this.header){
19205             header = {
19206                 tag : 'table',
19207                 cls : 'fc-header',
19208                 style : 'width:100%',
19209                 cn : [
19210                     {
19211                         tag: 'tr',
19212                         cn : [
19213                             {
19214                                 tag : 'td',
19215                                 cls : 'fc-header-left',
19216                                 cn : [
19217                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19218                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19219                                     { tag: 'span', cls: 'fc-header-space' },
19220                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19221
19222
19223                                 ]
19224                             },
19225
19226                             {
19227                                 tag : 'td',
19228                                 cls : 'fc-header-center',
19229                                 cn : [
19230                                     {
19231                                         tag: 'span',
19232                                         cls: 'fc-header-title',
19233                                         cn : {
19234                                             tag: 'H2',
19235                                             html : 'month / year'
19236                                         }
19237                                     }
19238
19239                                 ]
19240                             },
19241                             {
19242                                 tag : 'td',
19243                                 cls : 'fc-header-right',
19244                                 cn : [
19245                               /*      fc_button('month', 'left', '', 'month' ),
19246                                     fc_button('week', '', '', 'week' ),
19247                                     fc_button('day', 'right', '', 'day' )
19248                                 */    
19249
19250                                 ]
19251                             }
19252
19253                         ]
19254                     }
19255                 ]
19256             };
19257         }
19258         
19259         header = this.header;
19260         
19261        
19262         var cal_heads = function() {
19263             var ret = [];
19264             // fixme - handle this.
19265             
19266             for (var i =0; i < Date.dayNames.length; i++) {
19267                 var d = Date.dayNames[i];
19268                 ret.push({
19269                     tag: 'th',
19270                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19271                     html : d.substring(0,3)
19272                 });
19273                 
19274             }
19275             ret[0].cls += ' fc-first';
19276             ret[6].cls += ' fc-last';
19277             return ret;
19278         };
19279         var cal_cell = function(n) {
19280             return  {
19281                 tag: 'td',
19282                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19283                 cn : [
19284                     {
19285                         cn : [
19286                             {
19287                                 cls: 'fc-day-number',
19288                                 html: 'D'
19289                             },
19290                             {
19291                                 cls: 'fc-day-content',
19292                              
19293                                 cn : [
19294                                      {
19295                                         style: 'position: relative;' // height: 17px;
19296                                     }
19297                                 ]
19298                             }
19299                             
19300                             
19301                         ]
19302                     }
19303                 ]
19304                 
19305             }
19306         };
19307         var cal_rows = function() {
19308             
19309             var ret = [];
19310             for (var r = 0; r < 6; r++) {
19311                 var row= {
19312                     tag : 'tr',
19313                     cls : 'fc-week',
19314                     cn : []
19315                 };
19316                 
19317                 for (var i =0; i < Date.dayNames.length; i++) {
19318                     var d = Date.dayNames[i];
19319                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19320
19321                 }
19322                 row.cn[0].cls+=' fc-first';
19323                 row.cn[0].cn[0].style = 'min-height:90px';
19324                 row.cn[6].cls+=' fc-last';
19325                 ret.push(row);
19326                 
19327             }
19328             ret[0].cls += ' fc-first';
19329             ret[4].cls += ' fc-prev-last';
19330             ret[5].cls += ' fc-last';
19331             return ret;
19332             
19333         };
19334         
19335         var cal_table = {
19336             tag: 'table',
19337             cls: 'fc-border-separate',
19338             style : 'width:100%',
19339             cellspacing  : 0,
19340             cn : [
19341                 { 
19342                     tag: 'thead',
19343                     cn : [
19344                         { 
19345                             tag: 'tr',
19346                             cls : 'fc-first fc-last',
19347                             cn : cal_heads()
19348                         }
19349                     ]
19350                 },
19351                 { 
19352                     tag: 'tbody',
19353                     cn : cal_rows()
19354                 }
19355                   
19356             ]
19357         };
19358          
19359          var cfg = {
19360             cls : 'fc fc-ltr',
19361             cn : [
19362                 header,
19363                 {
19364                     cls : 'fc-content',
19365                     style : "position: relative;",
19366                     cn : [
19367                         {
19368                             cls : 'fc-view fc-view-month fc-grid',
19369                             style : 'position: relative',
19370                             unselectable : 'on',
19371                             cn : [
19372                                 {
19373                                     cls : 'fc-event-container',
19374                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19375                                 },
19376                                 cal_table
19377                             ]
19378                         }
19379                     ]
19380     
19381                 }
19382            ] 
19383             
19384         };
19385         
19386          
19387         
19388         return cfg;
19389     },
19390     
19391     
19392     initEvents : function()
19393     {
19394         if(!this.store){
19395             throw "can not find store for calendar";
19396         }
19397         
19398         var mark = {
19399             tag: "div",
19400             cls:"x-dlg-mask",
19401             style: "text-align:center",
19402             cn: [
19403                 {
19404                     tag: "div",
19405                     style: "background-color:white;width:50%;margin:250 auto",
19406                     cn: [
19407                         {
19408                             tag: "img",
19409                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19410                         },
19411                         {
19412                             tag: "span",
19413                             html: "Loading"
19414                         }
19415                         
19416                     ]
19417                 }
19418             ]
19419         };
19420         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19421         
19422         var size = this.el.select('.fc-content', true).first().getSize();
19423         this.maskEl.setSize(size.width, size.height);
19424         this.maskEl.enableDisplayMode("block");
19425         if(!this.loadMask){
19426             this.maskEl.hide();
19427         }
19428         
19429         this.store = Roo.factory(this.store, Roo.data);
19430         this.store.on('load', this.onLoad, this);
19431         this.store.on('beforeload', this.onBeforeLoad, this);
19432         
19433         this.resize();
19434         
19435         this.cells = this.el.select('.fc-day',true);
19436         //Roo.log(this.cells);
19437         this.textNodes = this.el.query('.fc-day-number');
19438         this.cells.addClassOnOver('fc-state-hover');
19439         
19440         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19441         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19442         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19443         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19444         
19445         this.on('monthchange', this.onMonthChange, this);
19446         
19447         this.update(new Date().clearTime());
19448     },
19449     
19450     resize : function() {
19451         var sz  = this.el.getSize();
19452         
19453         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19454         this.el.select('.fc-day-content div',true).setHeight(34);
19455     },
19456     
19457     
19458     // private
19459     showPrevMonth : function(e){
19460         this.update(this.activeDate.add("mo", -1));
19461     },
19462     showToday : function(e){
19463         this.update(new Date().clearTime());
19464     },
19465     // private
19466     showNextMonth : function(e){
19467         this.update(this.activeDate.add("mo", 1));
19468     },
19469
19470     // private
19471     showPrevYear : function(){
19472         this.update(this.activeDate.add("y", -1));
19473     },
19474
19475     // private
19476     showNextYear : function(){
19477         this.update(this.activeDate.add("y", 1));
19478     },
19479
19480     
19481    // private
19482     update : function(date)
19483     {
19484         var vd = this.activeDate;
19485         this.activeDate = date;
19486 //        if(vd && this.el){
19487 //            var t = date.getTime();
19488 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19489 //                Roo.log('using add remove');
19490 //                
19491 //                this.fireEvent('monthchange', this, date);
19492 //                
19493 //                this.cells.removeClass("fc-state-highlight");
19494 //                this.cells.each(function(c){
19495 //                   if(c.dateValue == t){
19496 //                       c.addClass("fc-state-highlight");
19497 //                       setTimeout(function(){
19498 //                            try{c.dom.firstChild.focus();}catch(e){}
19499 //                       }, 50);
19500 //                       return false;
19501 //                   }
19502 //                   return true;
19503 //                });
19504 //                return;
19505 //            }
19506 //        }
19507         
19508         var days = date.getDaysInMonth();
19509         
19510         var firstOfMonth = date.getFirstDateOfMonth();
19511         var startingPos = firstOfMonth.getDay()-this.startDay;
19512         
19513         if(startingPos < this.startDay){
19514             startingPos += 7;
19515         }
19516         
19517         var pm = date.add(Date.MONTH, -1);
19518         var prevStart = pm.getDaysInMonth()-startingPos;
19519 //        
19520         this.cells = this.el.select('.fc-day',true);
19521         this.textNodes = this.el.query('.fc-day-number');
19522         this.cells.addClassOnOver('fc-state-hover');
19523         
19524         var cells = this.cells.elements;
19525         var textEls = this.textNodes;
19526         
19527         Roo.each(cells, function(cell){
19528             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19529         });
19530         
19531         days += startingPos;
19532
19533         // convert everything to numbers so it's fast
19534         var day = 86400000;
19535         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19536         //Roo.log(d);
19537         //Roo.log(pm);
19538         //Roo.log(prevStart);
19539         
19540         var today = new Date().clearTime().getTime();
19541         var sel = date.clearTime().getTime();
19542         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19543         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19544         var ddMatch = this.disabledDatesRE;
19545         var ddText = this.disabledDatesText;
19546         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19547         var ddaysText = this.disabledDaysText;
19548         var format = this.format;
19549         
19550         var setCellClass = function(cal, cell){
19551             cell.row = 0;
19552             cell.events = [];
19553             cell.more = [];
19554             //Roo.log('set Cell Class');
19555             cell.title = "";
19556             var t = d.getTime();
19557             
19558             //Roo.log(d);
19559             
19560             cell.dateValue = t;
19561             if(t == today){
19562                 cell.className += " fc-today";
19563                 cell.className += " fc-state-highlight";
19564                 cell.title = cal.todayText;
19565             }
19566             if(t == sel){
19567                 // disable highlight in other month..
19568                 //cell.className += " fc-state-highlight";
19569                 
19570             }
19571             // disabling
19572             if(t < min) {
19573                 cell.className = " fc-state-disabled";
19574                 cell.title = cal.minText;
19575                 return;
19576             }
19577             if(t > max) {
19578                 cell.className = " fc-state-disabled";
19579                 cell.title = cal.maxText;
19580                 return;
19581             }
19582             if(ddays){
19583                 if(ddays.indexOf(d.getDay()) != -1){
19584                     cell.title = ddaysText;
19585                     cell.className = " fc-state-disabled";
19586                 }
19587             }
19588             if(ddMatch && format){
19589                 var fvalue = d.dateFormat(format);
19590                 if(ddMatch.test(fvalue)){
19591                     cell.title = ddText.replace("%0", fvalue);
19592                     cell.className = " fc-state-disabled";
19593                 }
19594             }
19595             
19596             if (!cell.initialClassName) {
19597                 cell.initialClassName = cell.dom.className;
19598             }
19599             
19600             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19601         };
19602
19603         var i = 0;
19604         
19605         for(; i < startingPos; i++) {
19606             textEls[i].innerHTML = (++prevStart);
19607             d.setDate(d.getDate()+1);
19608             
19609             cells[i].className = "fc-past fc-other-month";
19610             setCellClass(this, cells[i]);
19611         }
19612         
19613         var intDay = 0;
19614         
19615         for(; i < days; i++){
19616             intDay = i - startingPos + 1;
19617             textEls[i].innerHTML = (intDay);
19618             d.setDate(d.getDate()+1);
19619             
19620             cells[i].className = ''; // "x-date-active";
19621             setCellClass(this, cells[i]);
19622         }
19623         var extraDays = 0;
19624         
19625         for(; i < 42; i++) {
19626             textEls[i].innerHTML = (++extraDays);
19627             d.setDate(d.getDate()+1);
19628             
19629             cells[i].className = "fc-future fc-other-month";
19630             setCellClass(this, cells[i]);
19631         }
19632         
19633         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19634         
19635         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19636         
19637         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19638         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19639         
19640         if(totalRows != 6){
19641             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19642             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19643         }
19644         
19645         this.fireEvent('monthchange', this, date);
19646         
19647         
19648         /*
19649         if(!this.internalRender){
19650             var main = this.el.dom.firstChild;
19651             var w = main.offsetWidth;
19652             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19653             Roo.fly(main).setWidth(w);
19654             this.internalRender = true;
19655             // opera does not respect the auto grow header center column
19656             // then, after it gets a width opera refuses to recalculate
19657             // without a second pass
19658             if(Roo.isOpera && !this.secondPass){
19659                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19660                 this.secondPass = true;
19661                 this.update.defer(10, this, [date]);
19662             }
19663         }
19664         */
19665         
19666     },
19667     
19668     findCell : function(dt) {
19669         dt = dt.clearTime().getTime();
19670         var ret = false;
19671         this.cells.each(function(c){
19672             //Roo.log("check " +c.dateValue + '?=' + dt);
19673             if(c.dateValue == dt){
19674                 ret = c;
19675                 return false;
19676             }
19677             return true;
19678         });
19679         
19680         return ret;
19681     },
19682     
19683     findCells : function(ev) {
19684         var s = ev.start.clone().clearTime().getTime();
19685        // Roo.log(s);
19686         var e= ev.end.clone().clearTime().getTime();
19687        // Roo.log(e);
19688         var ret = [];
19689         this.cells.each(function(c){
19690              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19691             
19692             if(c.dateValue > e){
19693                 return ;
19694             }
19695             if(c.dateValue < s){
19696                 return ;
19697             }
19698             ret.push(c);
19699         });
19700         
19701         return ret;    
19702     },
19703     
19704 //    findBestRow: function(cells)
19705 //    {
19706 //        var ret = 0;
19707 //        
19708 //        for (var i =0 ; i < cells.length;i++) {
19709 //            ret  = Math.max(cells[i].rows || 0,ret);
19710 //        }
19711 //        return ret;
19712 //        
19713 //    },
19714     
19715     
19716     addItem : function(ev)
19717     {
19718         // look for vertical location slot in
19719         var cells = this.findCells(ev);
19720         
19721 //        ev.row = this.findBestRow(cells);
19722         
19723         // work out the location.
19724         
19725         var crow = false;
19726         var rows = [];
19727         for(var i =0; i < cells.length; i++) {
19728             
19729             cells[i].row = cells[0].row;
19730             
19731             if(i == 0){
19732                 cells[i].row = cells[i].row + 1;
19733             }
19734             
19735             if (!crow) {
19736                 crow = {
19737                     start : cells[i],
19738                     end :  cells[i]
19739                 };
19740                 continue;
19741             }
19742             if (crow.start.getY() == cells[i].getY()) {
19743                 // on same row.
19744                 crow.end = cells[i];
19745                 continue;
19746             }
19747             // different row.
19748             rows.push(crow);
19749             crow = {
19750                 start: cells[i],
19751                 end : cells[i]
19752             };
19753             
19754         }
19755         
19756         rows.push(crow);
19757         ev.els = [];
19758         ev.rows = rows;
19759         ev.cells = cells;
19760         
19761         cells[0].events.push(ev);
19762         
19763         this.calevents.push(ev);
19764     },
19765     
19766     clearEvents: function() {
19767         
19768         if(!this.calevents){
19769             return;
19770         }
19771         
19772         Roo.each(this.cells.elements, function(c){
19773             c.row = 0;
19774             c.events = [];
19775             c.more = [];
19776         });
19777         
19778         Roo.each(this.calevents, function(e) {
19779             Roo.each(e.els, function(el) {
19780                 el.un('mouseenter' ,this.onEventEnter, this);
19781                 el.un('mouseleave' ,this.onEventLeave, this);
19782                 el.remove();
19783             },this);
19784         },this);
19785         
19786         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19787             e.remove();
19788         });
19789         
19790     },
19791     
19792     renderEvents: function()
19793     {   
19794         var _this = this;
19795         
19796         this.cells.each(function(c) {
19797             
19798             if(c.row < 5){
19799                 return;
19800             }
19801             
19802             var ev = c.events;
19803             
19804             var r = 4;
19805             if(c.row != c.events.length){
19806                 r = 4 - (4 - (c.row - c.events.length));
19807             }
19808             
19809             c.events = ev.slice(0, r);
19810             c.more = ev.slice(r);
19811             
19812             if(c.more.length && c.more.length == 1){
19813                 c.events.push(c.more.pop());
19814             }
19815             
19816             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19817             
19818         });
19819             
19820         this.cells.each(function(c) {
19821             
19822             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19823             
19824             
19825             for (var e = 0; e < c.events.length; e++){
19826                 var ev = c.events[e];
19827                 var rows = ev.rows;
19828                 
19829                 for(var i = 0; i < rows.length; i++) {
19830                 
19831                     // how many rows should it span..
19832
19833                     var  cfg = {
19834                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19835                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19836
19837                         unselectable : "on",
19838                         cn : [
19839                             {
19840                                 cls: 'fc-event-inner',
19841                                 cn : [
19842     //                                {
19843     //                                  tag:'span',
19844     //                                  cls: 'fc-event-time',
19845     //                                  html : cells.length > 1 ? '' : ev.time
19846     //                                },
19847                                     {
19848                                       tag:'span',
19849                                       cls: 'fc-event-title',
19850                                       html : String.format('{0}', ev.title)
19851                                     }
19852
19853
19854                                 ]
19855                             },
19856                             {
19857                                 cls: 'ui-resizable-handle ui-resizable-e',
19858                                 html : '&nbsp;&nbsp;&nbsp'
19859                             }
19860
19861                         ]
19862                     };
19863
19864                     if (i == 0) {
19865                         cfg.cls += ' fc-event-start';
19866                     }
19867                     if ((i+1) == rows.length) {
19868                         cfg.cls += ' fc-event-end';
19869                     }
19870
19871                     var ctr = _this.el.select('.fc-event-container',true).first();
19872                     var cg = ctr.createChild(cfg);
19873
19874                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19875                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19876
19877                     var r = (c.more.length) ? 1 : 0;
19878                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19879                     cg.setWidth(ebox.right - sbox.x -2);
19880
19881                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19882                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19883                     cg.on('click', _this.onEventClick, _this, ev);
19884
19885                     ev.els.push(cg);
19886                     
19887                 }
19888                 
19889             }
19890             
19891             
19892             if(c.more.length){
19893                 var  cfg = {
19894                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19895                     style : 'position: absolute',
19896                     unselectable : "on",
19897                     cn : [
19898                         {
19899                             cls: 'fc-event-inner',
19900                             cn : [
19901                                 {
19902                                   tag:'span',
19903                                   cls: 'fc-event-title',
19904                                   html : 'More'
19905                                 }
19906
19907
19908                             ]
19909                         },
19910                         {
19911                             cls: 'ui-resizable-handle ui-resizable-e',
19912                             html : '&nbsp;&nbsp;&nbsp'
19913                         }
19914
19915                     ]
19916                 };
19917
19918                 var ctr = _this.el.select('.fc-event-container',true).first();
19919                 var cg = ctr.createChild(cfg);
19920
19921                 var sbox = c.select('.fc-day-content',true).first().getBox();
19922                 var ebox = c.select('.fc-day-content',true).first().getBox();
19923                 //Roo.log(cg);
19924                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19925                 cg.setWidth(ebox.right - sbox.x -2);
19926
19927                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19928                 
19929             }
19930             
19931         });
19932         
19933         
19934         
19935     },
19936     
19937     onEventEnter: function (e, el,event,d) {
19938         this.fireEvent('evententer', this, el, event);
19939     },
19940     
19941     onEventLeave: function (e, el,event,d) {
19942         this.fireEvent('eventleave', this, el, event);
19943     },
19944     
19945     onEventClick: function (e, el,event,d) {
19946         this.fireEvent('eventclick', this, el, event);
19947     },
19948     
19949     onMonthChange: function () {
19950         this.store.load();
19951     },
19952     
19953     onMoreEventClick: function(e, el, more)
19954     {
19955         var _this = this;
19956         
19957         this.calpopover.placement = 'right';
19958         this.calpopover.setTitle('More');
19959         
19960         this.calpopover.setContent('');
19961         
19962         var ctr = this.calpopover.el.select('.popover-content', true).first();
19963         
19964         Roo.each(more, function(m){
19965             var cfg = {
19966                 cls : 'fc-event-hori fc-event-draggable',
19967                 html : m.title
19968             };
19969             var cg = ctr.createChild(cfg);
19970             
19971             cg.on('click', _this.onEventClick, _this, m);
19972         });
19973         
19974         this.calpopover.show(el);
19975         
19976         
19977     },
19978     
19979     onLoad: function () 
19980     {   
19981         this.calevents = [];
19982         var cal = this;
19983         
19984         if(this.store.getCount() > 0){
19985             this.store.data.each(function(d){
19986                cal.addItem({
19987                     id : d.data.id,
19988                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19989                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19990                     time : d.data.start_time,
19991                     title : d.data.title,
19992                     description : d.data.description,
19993                     venue : d.data.venue
19994                 });
19995             });
19996         }
19997         
19998         this.renderEvents();
19999         
20000         if(this.calevents.length && this.loadMask){
20001             this.maskEl.hide();
20002         }
20003     },
20004     
20005     onBeforeLoad: function()
20006     {
20007         this.clearEvents();
20008         if(this.loadMask){
20009             this.maskEl.show();
20010         }
20011     }
20012 });
20013
20014  
20015  /*
20016  * - LGPL
20017  *
20018  * element
20019  * 
20020  */
20021
20022 /**
20023  * @class Roo.bootstrap.Popover
20024  * @extends Roo.bootstrap.Component
20025  * Bootstrap Popover class
20026  * @cfg {String} html contents of the popover   (or false to use children..)
20027  * @cfg {String} title of popover (or false to hide)
20028  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20029  * @cfg {String} trigger click || hover (or false to trigger manually)
20030  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20031  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20032  *      - if false and it has a 'parent' then it will be automatically added to that element
20033  *      - if string - Roo.get  will be called 
20034  * @cfg {Number} delay - delay before showing
20035  
20036  * @constructor
20037  * Create a new Popover
20038  * @param {Object} config The config object
20039  */
20040
20041 Roo.bootstrap.Popover = function(config){
20042     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20043     
20044     this.addEvents({
20045         // raw events
20046          /**
20047          * @event show
20048          * After the popover show
20049          * 
20050          * @param {Roo.bootstrap.Popover} this
20051          */
20052         "show" : true,
20053         /**
20054          * @event hide
20055          * After the popover hide
20056          * 
20057          * @param {Roo.bootstrap.Popover} this
20058          */
20059         "hide" : true
20060     });
20061 };
20062
20063 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20064     
20065     title: false,
20066     html: false,
20067     
20068     placement : 'right',
20069     trigger : 'hover', // hover
20070     modal : false,
20071     delay : 0,
20072     
20073     over: false,
20074     
20075     can_build_overlaid : false,
20076     
20077     maskEl : false, // the mask element
20078     headerEl : false,
20079     contentEl : false,
20080     alignEl : false, // when show is called with an element - this get's stored.
20081     
20082     getChildContainer : function()
20083     {
20084         return this.contentEl;
20085         
20086     },
20087     getPopoverHeader : function()
20088     {
20089         this.title = true; // flag not to hide it..
20090         this.headerEl.addClass('p-0');
20091         return this.headerEl
20092     },
20093     
20094     
20095     getAutoCreate : function(){
20096          
20097         var cfg = {
20098            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20099            style: 'display:block',
20100            cn : [
20101                 {
20102                     cls : 'arrow'
20103                 },
20104                 {
20105                     cls : 'popover-inner ',
20106                     cn : [
20107                         {
20108                             tag: 'h3',
20109                             cls: 'popover-title popover-header',
20110                             html : this.title === false ? '' : this.title
20111                         },
20112                         {
20113                             cls : 'popover-content popover-body '  + (this.cls || ''),
20114                             html : this.html || ''
20115                         }
20116                     ]
20117                     
20118                 }
20119            ]
20120         };
20121         
20122         return cfg;
20123     },
20124     /**
20125      * @param {string} the title
20126      */
20127     setTitle: function(str)
20128     {
20129         this.title = str;
20130         if (this.el) {
20131             this.headerEl.dom.innerHTML = str;
20132         }
20133         
20134     },
20135     /**
20136      * @param {string} the body content
20137      */
20138     setContent: function(str)
20139     {
20140         this.html = str;
20141         if (this.contentEl) {
20142             this.contentEl.dom.innerHTML = str;
20143         }
20144         
20145     },
20146     // as it get's added to the bottom of the page.
20147     onRender : function(ct, position)
20148     {
20149         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20150         
20151         
20152         
20153         if(!this.el){
20154             var cfg = Roo.apply({},  this.getAutoCreate());
20155             cfg.id = Roo.id();
20156             
20157             if (this.cls) {
20158                 cfg.cls += ' ' + this.cls;
20159             }
20160             if (this.style) {
20161                 cfg.style = this.style;
20162             }
20163             //Roo.log("adding to ");
20164             this.el = Roo.get(document.body).createChild(cfg, position);
20165 //            Roo.log(this.el);
20166         }
20167         
20168         this.contentEl = this.el.select('.popover-content',true).first();
20169         this.headerEl =  this.el.select('.popover-title',true).first();
20170         
20171         var nitems = [];
20172         if(typeof(this.items) != 'undefined'){
20173             var items = this.items;
20174             delete this.items;
20175
20176             for(var i =0;i < items.length;i++) {
20177                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20178             }
20179         }
20180
20181         this.items = nitems;
20182         
20183         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20184         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20185         
20186         
20187         
20188         this.initEvents();
20189     },
20190     
20191     resizeMask : function()
20192     {
20193         this.maskEl.setSize(
20194             Roo.lib.Dom.getViewWidth(true),
20195             Roo.lib.Dom.getViewHeight(true)
20196         );
20197     },
20198     
20199     initEvents : function()
20200     {
20201         
20202         if (!this.modal) { 
20203             Roo.bootstrap.Popover.register(this);
20204         }
20205          
20206         this.arrowEl = this.el.select('.arrow',true).first();
20207         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20208         this.el.enableDisplayMode('block');
20209         this.el.hide();
20210  
20211         
20212         if (this.over === false && !this.parent()) {
20213             return; 
20214         }
20215         if (this.triggers === false) {
20216             return;
20217         }
20218          
20219         // support parent
20220         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20221         var triggers = this.trigger ? this.trigger.split(' ') : [];
20222         Roo.each(triggers, function(trigger) {
20223         
20224             if (trigger == 'click') {
20225                 on_el.on('click', this.toggle, this);
20226             } else if (trigger != 'manual') {
20227                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20228                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20229       
20230                 on_el.on(eventIn  ,this.enter, this);
20231                 on_el.on(eventOut, this.leave, this);
20232             }
20233         }, this);
20234     },
20235     
20236     
20237     // private
20238     timeout : null,
20239     hoverState : null,
20240     
20241     toggle : function () {
20242         this.hoverState == 'in' ? this.leave() : this.enter();
20243     },
20244     
20245     enter : function () {
20246         
20247         clearTimeout(this.timeout);
20248     
20249         this.hoverState = 'in';
20250     
20251         if (!this.delay || !this.delay.show) {
20252             this.show();
20253             return;
20254         }
20255         var _t = this;
20256         this.timeout = setTimeout(function () {
20257             if (_t.hoverState == 'in') {
20258                 _t.show();
20259             }
20260         }, this.delay.show)
20261     },
20262     
20263     leave : function() {
20264         clearTimeout(this.timeout);
20265     
20266         this.hoverState = 'out';
20267     
20268         if (!this.delay || !this.delay.hide) {
20269             this.hide();
20270             return;
20271         }
20272         var _t = this;
20273         this.timeout = setTimeout(function () {
20274             if (_t.hoverState == 'out') {
20275                 _t.hide();
20276             }
20277         }, this.delay.hide)
20278     },
20279     /**
20280      * Show the popover
20281      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20282      * @param {string} (left|right|top|bottom) position
20283      */
20284     show : function (on_el, placement)
20285     {
20286         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20287         on_el = on_el || false; // default to false
20288          
20289         if (!on_el) {
20290             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20291                 on_el = this.parent().el;
20292             } else if (this.over) {
20293                 on_el = Roo.get(this.over);
20294             }
20295             
20296         }
20297         
20298         this.alignEl = Roo.get( on_el );
20299
20300         if (!this.el) {
20301             this.render(document.body);
20302         }
20303         
20304         
20305          
20306         
20307         if (this.title === false) {
20308             this.headerEl.hide();
20309         }
20310         
20311        
20312         this.el.show();
20313         this.el.dom.style.display = 'block';
20314          
20315  
20316         if (this.alignEl) {
20317             this.updatePosition(this.placement, true);
20318              
20319         } else {
20320             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20321             var es = this.el.getSize();
20322             var x = Roo.lib.Dom.getViewWidth()/2;
20323             var y = Roo.lib.Dom.getViewHeight()/2;
20324             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20325             
20326         }
20327
20328         
20329         //var arrow = this.el.select('.arrow',true).first();
20330         //arrow.set(align[2], 
20331         
20332         this.el.addClass('in');
20333         
20334          
20335         
20336         this.hoverState = 'in';
20337         
20338         if (this.modal) {
20339             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20340             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20341             this.maskEl.dom.style.display = 'block';
20342             this.maskEl.addClass('show');
20343         }
20344         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20345  
20346         this.fireEvent('show', this);
20347         
20348     },
20349     /**
20350      * fire this manually after loading a grid in the table for example
20351      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20352      * @param {Boolean} try and move it if we cant get right position.
20353      */
20354     updatePosition : function(placement, try_move)
20355     {
20356         // allow for calling with no parameters
20357         placement = placement   ? placement :  this.placement;
20358         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20359         
20360         this.el.removeClass([
20361             'fade','top','bottom', 'left', 'right','in',
20362             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20363         ]);
20364         this.el.addClass(placement + ' bs-popover-' + placement);
20365         
20366         if (!this.alignEl ) {
20367             return false;
20368         }
20369         
20370         switch (placement) {
20371             case 'right':
20372                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20373                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20374                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20375                     //normal display... or moved up/down.
20376                     this.el.setXY(offset);
20377                     var xy = this.alignEl.getAnchorXY('tr', false);
20378                     xy[0]+=2;xy[1]+=5;
20379                     this.arrowEl.setXY(xy);
20380                     return true;
20381                 }
20382                 // continue through...
20383                 return this.updatePosition('left', false);
20384                 
20385             
20386             case 'left':
20387                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20388                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20389                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20390                     //normal display... or moved up/down.
20391                     this.el.setXY(offset);
20392                     var xy = this.alignEl.getAnchorXY('tl', false);
20393                     xy[0]-=10;xy[1]+=5; // << fix me
20394                     this.arrowEl.setXY(xy);
20395                     return true;
20396                 }
20397                 // call self...
20398                 return this.updatePosition('right', false);
20399             
20400             case 'top':
20401                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20402                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20403                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20404                     //normal display... or moved up/down.
20405                     this.el.setXY(offset);
20406                     var xy = this.alignEl.getAnchorXY('t', false);
20407                     xy[1]-=10; // << fix me
20408                     this.arrowEl.setXY(xy);
20409                     return true;
20410                 }
20411                 // fall through
20412                return this.updatePosition('bottom', false);
20413             
20414             case 'bottom':
20415                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20416                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20417                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20418                     //normal display... or moved up/down.
20419                     this.el.setXY(offset);
20420                     var xy = this.alignEl.getAnchorXY('b', false);
20421                      xy[1]+=2; // << fix me
20422                     this.arrowEl.setXY(xy);
20423                     return true;
20424                 }
20425                 // fall through
20426                 return this.updatePosition('top', false);
20427                 
20428             
20429         }
20430         
20431         
20432         return false;
20433     },
20434     
20435     hide : function()
20436     {
20437         this.el.setXY([0,0]);
20438         this.el.removeClass('in');
20439         this.el.hide();
20440         this.hoverState = null;
20441         this.maskEl.hide(); // always..
20442         this.fireEvent('hide', this);
20443     }
20444     
20445 });
20446
20447
20448 Roo.apply(Roo.bootstrap.Popover, {
20449
20450     alignment : {
20451         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20452         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20453         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20454         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20455     },
20456     
20457     zIndex : 20001,
20458
20459     clickHander : false,
20460     
20461     
20462
20463     onMouseDown : function(e)
20464     {
20465         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20466             /// what is nothing is showing..
20467             this.hideAll();
20468         }
20469          
20470     },
20471     
20472     
20473     popups : [],
20474     
20475     register : function(popup)
20476     {
20477         if (!Roo.bootstrap.Popover.clickHandler) {
20478             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20479         }
20480         // hide other popups.
20481         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20482         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20483         this.hideAll(); //<< why?
20484         //this.popups.push(popup);
20485     },
20486     hideAll : function()
20487     {
20488         this.popups.forEach(function(p) {
20489             p.hide();
20490         });
20491     },
20492     onShow : function() {
20493         Roo.bootstrap.Popover.popups.push(this);
20494     },
20495     onHide : function() {
20496         Roo.bootstrap.Popover.popups.remove(this);
20497     } 
20498
20499 });/*
20500  * - LGPL
20501  *
20502  * Card header - holder for the card header elements.
20503  * 
20504  */
20505
20506 /**
20507  * @class Roo.bootstrap.PopoverNav
20508  * @extends Roo.bootstrap.NavGroup
20509  * Bootstrap Popover header navigation class
20510  * @constructor
20511  * Create a new Popover Header Navigation 
20512  * @param {Object} config The config object
20513  */
20514
20515 Roo.bootstrap.PopoverNav = function(config){
20516     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20517 };
20518
20519 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20520     
20521     
20522     container_method : 'getPopoverHeader' 
20523     
20524      
20525     
20526     
20527    
20528 });
20529
20530  
20531
20532  /*
20533  * - LGPL
20534  *
20535  * Progress
20536  * 
20537  */
20538
20539 /**
20540  * @class Roo.bootstrap.Progress
20541  * @extends Roo.bootstrap.Component
20542  * Bootstrap Progress class
20543  * @cfg {Boolean} striped striped of the progress bar
20544  * @cfg {Boolean} active animated of the progress bar
20545  * 
20546  * 
20547  * @constructor
20548  * Create a new Progress
20549  * @param {Object} config The config object
20550  */
20551
20552 Roo.bootstrap.Progress = function(config){
20553     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20554 };
20555
20556 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20557     
20558     striped : false,
20559     active: false,
20560     
20561     getAutoCreate : function(){
20562         var cfg = {
20563             tag: 'div',
20564             cls: 'progress'
20565         };
20566         
20567         
20568         if(this.striped){
20569             cfg.cls += ' progress-striped';
20570         }
20571       
20572         if(this.active){
20573             cfg.cls += ' active';
20574         }
20575         
20576         
20577         return cfg;
20578     }
20579    
20580 });
20581
20582  
20583
20584  /*
20585  * - LGPL
20586  *
20587  * ProgressBar
20588  * 
20589  */
20590
20591 /**
20592  * @class Roo.bootstrap.ProgressBar
20593  * @extends Roo.bootstrap.Component
20594  * Bootstrap ProgressBar class
20595  * @cfg {Number} aria_valuenow aria-value now
20596  * @cfg {Number} aria_valuemin aria-value min
20597  * @cfg {Number} aria_valuemax aria-value max
20598  * @cfg {String} label label for the progress bar
20599  * @cfg {String} panel (success | info | warning | danger )
20600  * @cfg {String} role role of the progress bar
20601  * @cfg {String} sr_only text
20602  * 
20603  * 
20604  * @constructor
20605  * Create a new ProgressBar
20606  * @param {Object} config The config object
20607  */
20608
20609 Roo.bootstrap.ProgressBar = function(config){
20610     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20611 };
20612
20613 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20614     
20615     aria_valuenow : 0,
20616     aria_valuemin : 0,
20617     aria_valuemax : 100,
20618     label : false,
20619     panel : false,
20620     role : false,
20621     sr_only: false,
20622     
20623     getAutoCreate : function()
20624     {
20625         
20626         var cfg = {
20627             tag: 'div',
20628             cls: 'progress-bar',
20629             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20630         };
20631         
20632         if(this.sr_only){
20633             cfg.cn = {
20634                 tag: 'span',
20635                 cls: 'sr-only',
20636                 html: this.sr_only
20637             }
20638         }
20639         
20640         if(this.role){
20641             cfg.role = this.role;
20642         }
20643         
20644         if(this.aria_valuenow){
20645             cfg['aria-valuenow'] = this.aria_valuenow;
20646         }
20647         
20648         if(this.aria_valuemin){
20649             cfg['aria-valuemin'] = this.aria_valuemin;
20650         }
20651         
20652         if(this.aria_valuemax){
20653             cfg['aria-valuemax'] = this.aria_valuemax;
20654         }
20655         
20656         if(this.label && !this.sr_only){
20657             cfg.html = this.label;
20658         }
20659         
20660         if(this.panel){
20661             cfg.cls += ' progress-bar-' + this.panel;
20662         }
20663         
20664         return cfg;
20665     },
20666     
20667     update : function(aria_valuenow)
20668     {
20669         this.aria_valuenow = aria_valuenow;
20670         
20671         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20672     }
20673    
20674 });
20675
20676  
20677
20678  /*
20679  * - LGPL
20680  *
20681  * column
20682  * 
20683  */
20684
20685 /**
20686  * @class Roo.bootstrap.TabGroup
20687  * @extends Roo.bootstrap.Column
20688  * Bootstrap Column class
20689  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20690  * @cfg {Boolean} carousel true to make the group behave like a carousel
20691  * @cfg {Boolean} bullets show bullets for the panels
20692  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20693  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20694  * @cfg {Boolean} showarrow (true|false) show arrow default true
20695  * 
20696  * @constructor
20697  * Create a new TabGroup
20698  * @param {Object} config The config object
20699  */
20700
20701 Roo.bootstrap.TabGroup = function(config){
20702     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20703     if (!this.navId) {
20704         this.navId = Roo.id();
20705     }
20706     this.tabs = [];
20707     Roo.bootstrap.TabGroup.register(this);
20708     
20709 };
20710
20711 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20712     
20713     carousel : false,
20714     transition : false,
20715     bullets : 0,
20716     timer : 0,
20717     autoslide : false,
20718     slideFn : false,
20719     slideOnTouch : false,
20720     showarrow : true,
20721     
20722     getAutoCreate : function()
20723     {
20724         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20725         
20726         cfg.cls += ' tab-content';
20727         
20728         if (this.carousel) {
20729             cfg.cls += ' carousel slide';
20730             
20731             cfg.cn = [{
20732                cls : 'carousel-inner',
20733                cn : []
20734             }];
20735         
20736             if(this.bullets  && !Roo.isTouch){
20737                 
20738                 var bullets = {
20739                     cls : 'carousel-bullets',
20740                     cn : []
20741                 };
20742                
20743                 if(this.bullets_cls){
20744                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20745                 }
20746                 
20747                 bullets.cn.push({
20748                     cls : 'clear'
20749                 });
20750                 
20751                 cfg.cn[0].cn.push(bullets);
20752             }
20753             
20754             if(this.showarrow){
20755                 cfg.cn[0].cn.push({
20756                     tag : 'div',
20757                     class : 'carousel-arrow',
20758                     cn : [
20759                         {
20760                             tag : 'div',
20761                             class : 'carousel-prev',
20762                             cn : [
20763                                 {
20764                                     tag : 'i',
20765                                     class : 'fa fa-chevron-left'
20766                                 }
20767                             ]
20768                         },
20769                         {
20770                             tag : 'div',
20771                             class : 'carousel-next',
20772                             cn : [
20773                                 {
20774                                     tag : 'i',
20775                                     class : 'fa fa-chevron-right'
20776                                 }
20777                             ]
20778                         }
20779                     ]
20780                 });
20781             }
20782             
20783         }
20784         
20785         return cfg;
20786     },
20787     
20788     initEvents:  function()
20789     {
20790 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20791 //            this.el.on("touchstart", this.onTouchStart, this);
20792 //        }
20793         
20794         if(this.autoslide){
20795             var _this = this;
20796             
20797             this.slideFn = window.setInterval(function() {
20798                 _this.showPanelNext();
20799             }, this.timer);
20800         }
20801         
20802         if(this.showarrow){
20803             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20804             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20805         }
20806         
20807         
20808     },
20809     
20810 //    onTouchStart : function(e, el, o)
20811 //    {
20812 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20813 //            return;
20814 //        }
20815 //        
20816 //        this.showPanelNext();
20817 //    },
20818     
20819     
20820     getChildContainer : function()
20821     {
20822         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20823     },
20824     
20825     /**
20826     * register a Navigation item
20827     * @param {Roo.bootstrap.NavItem} the navitem to add
20828     */
20829     register : function(item)
20830     {
20831         this.tabs.push( item);
20832         item.navId = this.navId; // not really needed..
20833         this.addBullet();
20834     
20835     },
20836     
20837     getActivePanel : function()
20838     {
20839         var r = false;
20840         Roo.each(this.tabs, function(t) {
20841             if (t.active) {
20842                 r = t;
20843                 return false;
20844             }
20845             return null;
20846         });
20847         return r;
20848         
20849     },
20850     getPanelByName : function(n)
20851     {
20852         var r = false;
20853         Roo.each(this.tabs, function(t) {
20854             if (t.tabId == n) {
20855                 r = t;
20856                 return false;
20857             }
20858             return null;
20859         });
20860         return r;
20861     },
20862     indexOfPanel : function(p)
20863     {
20864         var r = false;
20865         Roo.each(this.tabs, function(t,i) {
20866             if (t.tabId == p.tabId) {
20867                 r = i;
20868                 return false;
20869             }
20870             return null;
20871         });
20872         return r;
20873     },
20874     /**
20875      * show a specific panel
20876      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20877      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20878      */
20879     showPanel : function (pan)
20880     {
20881         if(this.transition || typeof(pan) == 'undefined'){
20882             Roo.log("waiting for the transitionend");
20883             return false;
20884         }
20885         
20886         if (typeof(pan) == 'number') {
20887             pan = this.tabs[pan];
20888         }
20889         
20890         if (typeof(pan) == 'string') {
20891             pan = this.getPanelByName(pan);
20892         }
20893         
20894         var cur = this.getActivePanel();
20895         
20896         if(!pan || !cur){
20897             Roo.log('pan or acitve pan is undefined');
20898             return false;
20899         }
20900         
20901         if (pan.tabId == this.getActivePanel().tabId) {
20902             return true;
20903         }
20904         
20905         if (false === cur.fireEvent('beforedeactivate')) {
20906             return false;
20907         }
20908         
20909         if(this.bullets > 0 && !Roo.isTouch){
20910             this.setActiveBullet(this.indexOfPanel(pan));
20911         }
20912         
20913         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20914             
20915             //class="carousel-item carousel-item-next carousel-item-left"
20916             
20917             this.transition = true;
20918             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20919             var lr = dir == 'next' ? 'left' : 'right';
20920             pan.el.addClass(dir); // or prev
20921             pan.el.addClass('carousel-item-' + dir); // or prev
20922             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20923             cur.el.addClass(lr); // or right
20924             pan.el.addClass(lr);
20925             cur.el.addClass('carousel-item-' +lr); // or right
20926             pan.el.addClass('carousel-item-' +lr);
20927             
20928             
20929             var _this = this;
20930             cur.el.on('transitionend', function() {
20931                 Roo.log("trans end?");
20932                 
20933                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20934                 pan.setActive(true);
20935                 
20936                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20937                 cur.setActive(false);
20938                 
20939                 _this.transition = false;
20940                 
20941             }, this, { single:  true } );
20942             
20943             return true;
20944         }
20945         
20946         cur.setActive(false);
20947         pan.setActive(true);
20948         
20949         return true;
20950         
20951     },
20952     showPanelNext : function()
20953     {
20954         var i = this.indexOfPanel(this.getActivePanel());
20955         
20956         if (i >= this.tabs.length - 1 && !this.autoslide) {
20957             return;
20958         }
20959         
20960         if (i >= this.tabs.length - 1 && this.autoslide) {
20961             i = -1;
20962         }
20963         
20964         this.showPanel(this.tabs[i+1]);
20965     },
20966     
20967     showPanelPrev : function()
20968     {
20969         var i = this.indexOfPanel(this.getActivePanel());
20970         
20971         if (i  < 1 && !this.autoslide) {
20972             return;
20973         }
20974         
20975         if (i < 1 && this.autoslide) {
20976             i = this.tabs.length;
20977         }
20978         
20979         this.showPanel(this.tabs[i-1]);
20980     },
20981     
20982     
20983     addBullet: function()
20984     {
20985         if(!this.bullets || Roo.isTouch){
20986             return;
20987         }
20988         var ctr = this.el.select('.carousel-bullets',true).first();
20989         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20990         var bullet = ctr.createChild({
20991             cls : 'bullet bullet-' + i
20992         },ctr.dom.lastChild);
20993         
20994         
20995         var _this = this;
20996         
20997         bullet.on('click', (function(e, el, o, ii, t){
20998
20999             e.preventDefault();
21000
21001             this.showPanel(ii);
21002
21003             if(this.autoslide && this.slideFn){
21004                 clearInterval(this.slideFn);
21005                 this.slideFn = window.setInterval(function() {
21006                     _this.showPanelNext();
21007                 }, this.timer);
21008             }
21009
21010         }).createDelegate(this, [i, bullet], true));
21011                 
21012         
21013     },
21014      
21015     setActiveBullet : function(i)
21016     {
21017         if(Roo.isTouch){
21018             return;
21019         }
21020         
21021         Roo.each(this.el.select('.bullet', true).elements, function(el){
21022             el.removeClass('selected');
21023         });
21024
21025         var bullet = this.el.select('.bullet-' + i, true).first();
21026         
21027         if(!bullet){
21028             return;
21029         }
21030         
21031         bullet.addClass('selected');
21032     }
21033     
21034     
21035   
21036 });
21037
21038  
21039
21040  
21041  
21042 Roo.apply(Roo.bootstrap.TabGroup, {
21043     
21044     groups: {},
21045      /**
21046     * register a Navigation Group
21047     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21048     */
21049     register : function(navgrp)
21050     {
21051         this.groups[navgrp.navId] = navgrp;
21052         
21053     },
21054     /**
21055     * fetch a Navigation Group based on the navigation ID
21056     * if one does not exist , it will get created.
21057     * @param {string} the navgroup to add
21058     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21059     */
21060     get: function(navId) {
21061         if (typeof(this.groups[navId]) == 'undefined') {
21062             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21063         }
21064         return this.groups[navId] ;
21065     }
21066     
21067     
21068     
21069 });
21070
21071  /*
21072  * - LGPL
21073  *
21074  * TabPanel
21075  * 
21076  */
21077
21078 /**
21079  * @class Roo.bootstrap.TabPanel
21080  * @extends Roo.bootstrap.Component
21081  * Bootstrap TabPanel class
21082  * @cfg {Boolean} active panel active
21083  * @cfg {String} html panel content
21084  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21085  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21086  * @cfg {String} href click to link..
21087  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21088  * 
21089  * 
21090  * @constructor
21091  * Create a new TabPanel
21092  * @param {Object} config The config object
21093  */
21094
21095 Roo.bootstrap.TabPanel = function(config){
21096     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21097     this.addEvents({
21098         /**
21099              * @event changed
21100              * Fires when the active status changes
21101              * @param {Roo.bootstrap.TabPanel} this
21102              * @param {Boolean} state the new state
21103             
21104          */
21105         'changed': true,
21106         /**
21107              * @event beforedeactivate
21108              * Fires before a tab is de-activated - can be used to do validation on a form.
21109              * @param {Roo.bootstrap.TabPanel} this
21110              * @return {Boolean} false if there is an error
21111             
21112          */
21113         'beforedeactivate': true
21114      });
21115     
21116     this.tabId = this.tabId || Roo.id();
21117   
21118 };
21119
21120 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21121     
21122     active: false,
21123     html: false,
21124     tabId: false,
21125     navId : false,
21126     href : '',
21127     touchSlide : false,
21128     getAutoCreate : function(){
21129         
21130         
21131         var cfg = {
21132             tag: 'div',
21133             // item is needed for carousel - not sure if it has any effect otherwise
21134             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21135             html: this.html || ''
21136         };
21137         
21138         if(this.active){
21139             cfg.cls += ' active';
21140         }
21141         
21142         if(this.tabId){
21143             cfg.tabId = this.tabId;
21144         }
21145         
21146         
21147         
21148         return cfg;
21149     },
21150     
21151     initEvents:  function()
21152     {
21153         var p = this.parent();
21154         
21155         this.navId = this.navId || p.navId;
21156         
21157         if (typeof(this.navId) != 'undefined') {
21158             // not really needed.. but just in case.. parent should be a NavGroup.
21159             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21160             
21161             tg.register(this);
21162             
21163             var i = tg.tabs.length - 1;
21164             
21165             if(this.active && tg.bullets > 0 && i < tg.bullets){
21166                 tg.setActiveBullet(i);
21167             }
21168         }
21169         
21170         this.el.on('click', this.onClick, this);
21171         
21172         if(Roo.isTouch && this.touchSlide){
21173             this.el.on("touchstart", this.onTouchStart, this);
21174             this.el.on("touchmove", this.onTouchMove, this);
21175             this.el.on("touchend", this.onTouchEnd, this);
21176         }
21177         
21178     },
21179     
21180     onRender : function(ct, position)
21181     {
21182         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21183     },
21184     
21185     setActive : function(state)
21186     {
21187         Roo.log("panel - set active " + this.tabId + "=" + state);
21188         
21189         this.active = state;
21190         if (!state) {
21191             this.el.removeClass('active');
21192             
21193         } else  if (!this.el.hasClass('active')) {
21194             this.el.addClass('active');
21195         }
21196         
21197         this.fireEvent('changed', this, state);
21198     },
21199     
21200     onClick : function(e)
21201     {
21202         e.preventDefault();
21203         
21204         if(!this.href.length){
21205             return;
21206         }
21207         
21208         window.location.href = this.href;
21209     },
21210     
21211     startX : 0,
21212     startY : 0,
21213     endX : 0,
21214     endY : 0,
21215     swiping : false,
21216     
21217     onTouchStart : function(e)
21218     {
21219         this.swiping = false;
21220         
21221         this.startX = e.browserEvent.touches[0].clientX;
21222         this.startY = e.browserEvent.touches[0].clientY;
21223     },
21224     
21225     onTouchMove : function(e)
21226     {
21227         this.swiping = true;
21228         
21229         this.endX = e.browserEvent.touches[0].clientX;
21230         this.endY = e.browserEvent.touches[0].clientY;
21231     },
21232     
21233     onTouchEnd : function(e)
21234     {
21235         if(!this.swiping){
21236             this.onClick(e);
21237             return;
21238         }
21239         
21240         var tabGroup = this.parent();
21241         
21242         if(this.endX > this.startX){ // swiping right
21243             tabGroup.showPanelPrev();
21244             return;
21245         }
21246         
21247         if(this.startX > this.endX){ // swiping left
21248             tabGroup.showPanelNext();
21249             return;
21250         }
21251     }
21252     
21253     
21254 });
21255  
21256
21257  
21258
21259  /*
21260  * - LGPL
21261  *
21262  * DateField
21263  * 
21264  */
21265
21266 /**
21267  * @class Roo.bootstrap.DateField
21268  * @extends Roo.bootstrap.Input
21269  * Bootstrap DateField class
21270  * @cfg {Number} weekStart default 0
21271  * @cfg {String} viewMode default empty, (months|years)
21272  * @cfg {String} minViewMode default empty, (months|years)
21273  * @cfg {Number} startDate default -Infinity
21274  * @cfg {Number} endDate default Infinity
21275  * @cfg {Boolean} todayHighlight default false
21276  * @cfg {Boolean} todayBtn default false
21277  * @cfg {Boolean} calendarWeeks default false
21278  * @cfg {Object} daysOfWeekDisabled default empty
21279  * @cfg {Boolean} singleMode default false (true | false)
21280  * 
21281  * @cfg {Boolean} keyboardNavigation default true
21282  * @cfg {String} language default en
21283  * 
21284  * @constructor
21285  * Create a new DateField
21286  * @param {Object} config The config object
21287  */
21288
21289 Roo.bootstrap.DateField = function(config){
21290     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21291      this.addEvents({
21292             /**
21293              * @event show
21294              * Fires when this field show.
21295              * @param {Roo.bootstrap.DateField} this
21296              * @param {Mixed} date The date value
21297              */
21298             show : true,
21299             /**
21300              * @event show
21301              * Fires when this field hide.
21302              * @param {Roo.bootstrap.DateField} this
21303              * @param {Mixed} date The date value
21304              */
21305             hide : true,
21306             /**
21307              * @event select
21308              * Fires when select a date.
21309              * @param {Roo.bootstrap.DateField} this
21310              * @param {Mixed} date The date value
21311              */
21312             select : true,
21313             /**
21314              * @event beforeselect
21315              * Fires when before select a date.
21316              * @param {Roo.bootstrap.DateField} this
21317              * @param {Mixed} date The date value
21318              */
21319             beforeselect : true
21320         });
21321 };
21322
21323 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21324     
21325     /**
21326      * @cfg {String} format
21327      * The default date format string which can be overriden for localization support.  The format must be
21328      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21329      */
21330     format : "m/d/y",
21331     /**
21332      * @cfg {String} altFormats
21333      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21334      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21335      */
21336     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21337     
21338     weekStart : 0,
21339     
21340     viewMode : '',
21341     
21342     minViewMode : '',
21343     
21344     todayHighlight : false,
21345     
21346     todayBtn: false,
21347     
21348     language: 'en',
21349     
21350     keyboardNavigation: true,
21351     
21352     calendarWeeks: false,
21353     
21354     startDate: -Infinity,
21355     
21356     endDate: Infinity,
21357     
21358     daysOfWeekDisabled: [],
21359     
21360     _events: [],
21361     
21362     singleMode : false,
21363     
21364     UTCDate: function()
21365     {
21366         return new Date(Date.UTC.apply(Date, arguments));
21367     },
21368     
21369     UTCToday: function()
21370     {
21371         var today = new Date();
21372         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21373     },
21374     
21375     getDate: function() {
21376             var d = this.getUTCDate();
21377             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21378     },
21379     
21380     getUTCDate: function() {
21381             return this.date;
21382     },
21383     
21384     setDate: function(d) {
21385             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21386     },
21387     
21388     setUTCDate: function(d) {
21389             this.date = d;
21390             this.setValue(this.formatDate(this.date));
21391     },
21392         
21393     onRender: function(ct, position)
21394     {
21395         
21396         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21397         
21398         this.language = this.language || 'en';
21399         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21400         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21401         
21402         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21403         this.format = this.format || 'm/d/y';
21404         this.isInline = false;
21405         this.isInput = true;
21406         this.component = this.el.select('.add-on', true).first() || false;
21407         this.component = (this.component && this.component.length === 0) ? false : this.component;
21408         this.hasInput = this.component && this.inputEl().length;
21409         
21410         if (typeof(this.minViewMode === 'string')) {
21411             switch (this.minViewMode) {
21412                 case 'months':
21413                     this.minViewMode = 1;
21414                     break;
21415                 case 'years':
21416                     this.minViewMode = 2;
21417                     break;
21418                 default:
21419                     this.minViewMode = 0;
21420                     break;
21421             }
21422         }
21423         
21424         if (typeof(this.viewMode === 'string')) {
21425             switch (this.viewMode) {
21426                 case 'months':
21427                     this.viewMode = 1;
21428                     break;
21429                 case 'years':
21430                     this.viewMode = 2;
21431                     break;
21432                 default:
21433                     this.viewMode = 0;
21434                     break;
21435             }
21436         }
21437                 
21438         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21439         
21440 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21441         
21442         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21443         
21444         this.picker().on('mousedown', this.onMousedown, this);
21445         this.picker().on('click', this.onClick, this);
21446         
21447         this.picker().addClass('datepicker-dropdown');
21448         
21449         this.startViewMode = this.viewMode;
21450         
21451         if(this.singleMode){
21452             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21453                 v.setVisibilityMode(Roo.Element.DISPLAY);
21454                 v.hide();
21455             });
21456             
21457             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21458                 v.setStyle('width', '189px');
21459             });
21460         }
21461         
21462         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21463             if(!this.calendarWeeks){
21464                 v.remove();
21465                 return;
21466             }
21467             
21468             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21469             v.attr('colspan', function(i, val){
21470                 return parseInt(val) + 1;
21471             });
21472         });
21473                         
21474         
21475         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21476         
21477         this.setStartDate(this.startDate);
21478         this.setEndDate(this.endDate);
21479         
21480         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21481         
21482         this.fillDow();
21483         this.fillMonths();
21484         this.update();
21485         this.showMode();
21486         
21487         if(this.isInline) {
21488             this.showPopup();
21489         }
21490     },
21491     
21492     picker : function()
21493     {
21494         return this.pickerEl;
21495 //        return this.el.select('.datepicker', true).first();
21496     },
21497     
21498     fillDow: function()
21499     {
21500         var dowCnt = this.weekStart;
21501         
21502         var dow = {
21503             tag: 'tr',
21504             cn: [
21505                 
21506             ]
21507         };
21508         
21509         if(this.calendarWeeks){
21510             dow.cn.push({
21511                 tag: 'th',
21512                 cls: 'cw',
21513                 html: '&nbsp;'
21514             })
21515         }
21516         
21517         while (dowCnt < this.weekStart + 7) {
21518             dow.cn.push({
21519                 tag: 'th',
21520                 cls: 'dow',
21521                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21522             });
21523         }
21524         
21525         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21526     },
21527     
21528     fillMonths: function()
21529     {    
21530         var i = 0;
21531         var months = this.picker().select('>.datepicker-months td', true).first();
21532         
21533         months.dom.innerHTML = '';
21534         
21535         while (i < 12) {
21536             var month = {
21537                 tag: 'span',
21538                 cls: 'month',
21539                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21540             };
21541             
21542             months.createChild(month);
21543         }
21544         
21545     },
21546     
21547     update: function()
21548     {
21549         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;
21550         
21551         if (this.date < this.startDate) {
21552             this.viewDate = new Date(this.startDate);
21553         } else if (this.date > this.endDate) {
21554             this.viewDate = new Date(this.endDate);
21555         } else {
21556             this.viewDate = new Date(this.date);
21557         }
21558         
21559         this.fill();
21560     },
21561     
21562     fill: function() 
21563     {
21564         var d = new Date(this.viewDate),
21565                 year = d.getUTCFullYear(),
21566                 month = d.getUTCMonth(),
21567                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21568                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21569                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21570                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21571                 currentDate = this.date && this.date.valueOf(),
21572                 today = this.UTCToday();
21573         
21574         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21575         
21576 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21577         
21578 //        this.picker.select('>tfoot th.today').
21579 //                                              .text(dates[this.language].today)
21580 //                                              .toggle(this.todayBtn !== false);
21581     
21582         this.updateNavArrows();
21583         this.fillMonths();
21584                                                 
21585         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21586         
21587         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21588          
21589         prevMonth.setUTCDate(day);
21590         
21591         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21592         
21593         var nextMonth = new Date(prevMonth);
21594         
21595         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21596         
21597         nextMonth = nextMonth.valueOf();
21598         
21599         var fillMonths = false;
21600         
21601         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21602         
21603         while(prevMonth.valueOf() <= nextMonth) {
21604             var clsName = '';
21605             
21606             if (prevMonth.getUTCDay() === this.weekStart) {
21607                 if(fillMonths){
21608                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21609                 }
21610                     
21611                 fillMonths = {
21612                     tag: 'tr',
21613                     cn: []
21614                 };
21615                 
21616                 if(this.calendarWeeks){
21617                     // ISO 8601: First week contains first thursday.
21618                     // ISO also states week starts on Monday, but we can be more abstract here.
21619                     var
21620                     // Start of current week: based on weekstart/current date
21621                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21622                     // Thursday of this week
21623                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21624                     // First Thursday of year, year from thursday
21625                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21626                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21627                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21628                     
21629                     fillMonths.cn.push({
21630                         tag: 'td',
21631                         cls: 'cw',
21632                         html: calWeek
21633                     });
21634                 }
21635             }
21636             
21637             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21638                 clsName += ' old';
21639             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21640                 clsName += ' new';
21641             }
21642             if (this.todayHighlight &&
21643                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21644                 prevMonth.getUTCMonth() == today.getMonth() &&
21645                 prevMonth.getUTCDate() == today.getDate()) {
21646                 clsName += ' today';
21647             }
21648             
21649             if (currentDate && prevMonth.valueOf() === currentDate) {
21650                 clsName += ' active';
21651             }
21652             
21653             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21654                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21655                     clsName += ' disabled';
21656             }
21657             
21658             fillMonths.cn.push({
21659                 tag: 'td',
21660                 cls: 'day ' + clsName,
21661                 html: prevMonth.getDate()
21662             });
21663             
21664             prevMonth.setDate(prevMonth.getDate()+1);
21665         }
21666           
21667         var currentYear = this.date && this.date.getUTCFullYear();
21668         var currentMonth = this.date && this.date.getUTCMonth();
21669         
21670         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21671         
21672         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21673             v.removeClass('active');
21674             
21675             if(currentYear === year && k === currentMonth){
21676                 v.addClass('active');
21677             }
21678             
21679             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21680                 v.addClass('disabled');
21681             }
21682             
21683         });
21684         
21685         
21686         year = parseInt(year/10, 10) * 10;
21687         
21688         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21689         
21690         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21691         
21692         year -= 1;
21693         for (var i = -1; i < 11; i++) {
21694             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21695                 tag: 'span',
21696                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21697                 html: year
21698             });
21699             
21700             year += 1;
21701         }
21702     },
21703     
21704     showMode: function(dir) 
21705     {
21706         if (dir) {
21707             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21708         }
21709         
21710         Roo.each(this.picker().select('>div',true).elements, function(v){
21711             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21712             v.hide();
21713         });
21714         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21715     },
21716     
21717     place: function()
21718     {
21719         if(this.isInline) {
21720             return;
21721         }
21722         
21723         this.picker().removeClass(['bottom', 'top']);
21724         
21725         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21726             /*
21727              * place to the top of element!
21728              *
21729              */
21730             
21731             this.picker().addClass('top');
21732             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21733             
21734             return;
21735         }
21736         
21737         this.picker().addClass('bottom');
21738         
21739         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21740     },
21741     
21742     parseDate : function(value)
21743     {
21744         if(!value || value instanceof Date){
21745             return value;
21746         }
21747         var v = Date.parseDate(value, this.format);
21748         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21749             v = Date.parseDate(value, 'Y-m-d');
21750         }
21751         if(!v && this.altFormats){
21752             if(!this.altFormatsArray){
21753                 this.altFormatsArray = this.altFormats.split("|");
21754             }
21755             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21756                 v = Date.parseDate(value, this.altFormatsArray[i]);
21757             }
21758         }
21759         return v;
21760     },
21761     
21762     formatDate : function(date, fmt)
21763     {   
21764         return (!date || !(date instanceof Date)) ?
21765         date : date.dateFormat(fmt || this.format);
21766     },
21767     
21768     onFocus : function()
21769     {
21770         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21771         this.showPopup();
21772     },
21773     
21774     onBlur : function()
21775     {
21776         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21777         
21778         var d = this.inputEl().getValue();
21779         
21780         this.setValue(d);
21781                 
21782         this.hidePopup();
21783     },
21784     
21785     showPopup : function()
21786     {
21787         this.picker().show();
21788         this.update();
21789         this.place();
21790         
21791         this.fireEvent('showpopup', this, this.date);
21792     },
21793     
21794     hidePopup : function()
21795     {
21796         if(this.isInline) {
21797             return;
21798         }
21799         this.picker().hide();
21800         this.viewMode = this.startViewMode;
21801         this.showMode();
21802         
21803         this.fireEvent('hidepopup', this, this.date);
21804         
21805     },
21806     
21807     onMousedown: function(e)
21808     {
21809         e.stopPropagation();
21810         e.preventDefault();
21811     },
21812     
21813     keyup: function(e)
21814     {
21815         Roo.bootstrap.DateField.superclass.keyup.call(this);
21816         this.update();
21817     },
21818
21819     setValue: function(v)
21820     {
21821         if(this.fireEvent('beforeselect', this, v) !== false){
21822             var d = new Date(this.parseDate(v) ).clearTime();
21823         
21824             if(isNaN(d.getTime())){
21825                 this.date = this.viewDate = '';
21826                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21827                 return;
21828             }
21829
21830             v = this.formatDate(d);
21831
21832             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21833
21834             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21835
21836             this.update();
21837
21838             this.fireEvent('select', this, this.date);
21839         }
21840     },
21841     
21842     getValue: function()
21843     {
21844         return this.formatDate(this.date);
21845     },
21846     
21847     fireKey: function(e)
21848     {
21849         if (!this.picker().isVisible()){
21850             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21851                 this.showPopup();
21852             }
21853             return;
21854         }
21855         
21856         var dateChanged = false,
21857         dir, day, month,
21858         newDate, newViewDate;
21859         
21860         switch(e.keyCode){
21861             case 27: // escape
21862                 this.hidePopup();
21863                 e.preventDefault();
21864                 break;
21865             case 37: // left
21866             case 39: // right
21867                 if (!this.keyboardNavigation) {
21868                     break;
21869                 }
21870                 dir = e.keyCode == 37 ? -1 : 1;
21871                 
21872                 if (e.ctrlKey){
21873                     newDate = this.moveYear(this.date, dir);
21874                     newViewDate = this.moveYear(this.viewDate, dir);
21875                 } else if (e.shiftKey){
21876                     newDate = this.moveMonth(this.date, dir);
21877                     newViewDate = this.moveMonth(this.viewDate, dir);
21878                 } else {
21879                     newDate = new Date(this.date);
21880                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21881                     newViewDate = new Date(this.viewDate);
21882                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21883                 }
21884                 if (this.dateWithinRange(newDate)){
21885                     this.date = newDate;
21886                     this.viewDate = newViewDate;
21887                     this.setValue(this.formatDate(this.date));
21888 //                    this.update();
21889                     e.preventDefault();
21890                     dateChanged = true;
21891                 }
21892                 break;
21893             case 38: // up
21894             case 40: // down
21895                 if (!this.keyboardNavigation) {
21896                     break;
21897                 }
21898                 dir = e.keyCode == 38 ? -1 : 1;
21899                 if (e.ctrlKey){
21900                     newDate = this.moveYear(this.date, dir);
21901                     newViewDate = this.moveYear(this.viewDate, dir);
21902                 } else if (e.shiftKey){
21903                     newDate = this.moveMonth(this.date, dir);
21904                     newViewDate = this.moveMonth(this.viewDate, dir);
21905                 } else {
21906                     newDate = new Date(this.date);
21907                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21908                     newViewDate = new Date(this.viewDate);
21909                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21910                 }
21911                 if (this.dateWithinRange(newDate)){
21912                     this.date = newDate;
21913                     this.viewDate = newViewDate;
21914                     this.setValue(this.formatDate(this.date));
21915 //                    this.update();
21916                     e.preventDefault();
21917                     dateChanged = true;
21918                 }
21919                 break;
21920             case 13: // enter
21921                 this.setValue(this.formatDate(this.date));
21922                 this.hidePopup();
21923                 e.preventDefault();
21924                 break;
21925             case 9: // tab
21926                 this.setValue(this.formatDate(this.date));
21927                 this.hidePopup();
21928                 break;
21929             case 16: // shift
21930             case 17: // ctrl
21931             case 18: // alt
21932                 break;
21933             default :
21934                 this.hidePopup();
21935                 
21936         }
21937     },
21938     
21939     
21940     onClick: function(e) 
21941     {
21942         e.stopPropagation();
21943         e.preventDefault();
21944         
21945         var target = e.getTarget();
21946         
21947         if(target.nodeName.toLowerCase() === 'i'){
21948             target = Roo.get(target).dom.parentNode;
21949         }
21950         
21951         var nodeName = target.nodeName;
21952         var className = target.className;
21953         var html = target.innerHTML;
21954         //Roo.log(nodeName);
21955         
21956         switch(nodeName.toLowerCase()) {
21957             case 'th':
21958                 switch(className) {
21959                     case 'switch':
21960                         this.showMode(1);
21961                         break;
21962                     case 'prev':
21963                     case 'next':
21964                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21965                         switch(this.viewMode){
21966                                 case 0:
21967                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21968                                         break;
21969                                 case 1:
21970                                 case 2:
21971                                         this.viewDate = this.moveYear(this.viewDate, dir);
21972                                         break;
21973                         }
21974                         this.fill();
21975                         break;
21976                     case 'today':
21977                         var date = new Date();
21978                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21979 //                        this.fill()
21980                         this.setValue(this.formatDate(this.date));
21981                         
21982                         this.hidePopup();
21983                         break;
21984                 }
21985                 break;
21986             case 'span':
21987                 if (className.indexOf('disabled') < 0) {
21988                 if (!this.viewDate) {
21989                     this.viewDate = new Date();
21990                 }
21991                 this.viewDate.setUTCDate(1);
21992                     if (className.indexOf('month') > -1) {
21993                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21994                     } else {
21995                         var year = parseInt(html, 10) || 0;
21996                         this.viewDate.setUTCFullYear(year);
21997                         
21998                     }
21999                     
22000                     if(this.singleMode){
22001                         this.setValue(this.formatDate(this.viewDate));
22002                         this.hidePopup();
22003                         return;
22004                     }
22005                     
22006                     this.showMode(-1);
22007                     this.fill();
22008                 }
22009                 break;
22010                 
22011             case 'td':
22012                 //Roo.log(className);
22013                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22014                     var day = parseInt(html, 10) || 1;
22015                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22016                         month = (this.viewDate || new Date()).getUTCMonth();
22017
22018                     if (className.indexOf('old') > -1) {
22019                         if(month === 0 ){
22020                             month = 11;
22021                             year -= 1;
22022                         }else{
22023                             month -= 1;
22024                         }
22025                     } else if (className.indexOf('new') > -1) {
22026                         if (month == 11) {
22027                             month = 0;
22028                             year += 1;
22029                         } else {
22030                             month += 1;
22031                         }
22032                     }
22033                     //Roo.log([year,month,day]);
22034                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22035                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22036 //                    this.fill();
22037                     //Roo.log(this.formatDate(this.date));
22038                     this.setValue(this.formatDate(this.date));
22039                     this.hidePopup();
22040                 }
22041                 break;
22042         }
22043     },
22044     
22045     setStartDate: function(startDate)
22046     {
22047         this.startDate = startDate || -Infinity;
22048         if (this.startDate !== -Infinity) {
22049             this.startDate = this.parseDate(this.startDate);
22050         }
22051         this.update();
22052         this.updateNavArrows();
22053     },
22054
22055     setEndDate: function(endDate)
22056     {
22057         this.endDate = endDate || Infinity;
22058         if (this.endDate !== Infinity) {
22059             this.endDate = this.parseDate(this.endDate);
22060         }
22061         this.update();
22062         this.updateNavArrows();
22063     },
22064     
22065     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22066     {
22067         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22068         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22069             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22070         }
22071         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22072             return parseInt(d, 10);
22073         });
22074         this.update();
22075         this.updateNavArrows();
22076     },
22077     
22078     updateNavArrows: function() 
22079     {
22080         if(this.singleMode){
22081             return;
22082         }
22083         
22084         var d = new Date(this.viewDate),
22085         year = d.getUTCFullYear(),
22086         month = d.getUTCMonth();
22087         
22088         Roo.each(this.picker().select('.prev', true).elements, function(v){
22089             v.show();
22090             switch (this.viewMode) {
22091                 case 0:
22092
22093                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22094                         v.hide();
22095                     }
22096                     break;
22097                 case 1:
22098                 case 2:
22099                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22100                         v.hide();
22101                     }
22102                     break;
22103             }
22104         });
22105         
22106         Roo.each(this.picker().select('.next', true).elements, function(v){
22107             v.show();
22108             switch (this.viewMode) {
22109                 case 0:
22110
22111                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22112                         v.hide();
22113                     }
22114                     break;
22115                 case 1:
22116                 case 2:
22117                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22118                         v.hide();
22119                     }
22120                     break;
22121             }
22122         })
22123     },
22124     
22125     moveMonth: function(date, dir)
22126     {
22127         if (!dir) {
22128             return date;
22129         }
22130         var new_date = new Date(date.valueOf()),
22131         day = new_date.getUTCDate(),
22132         month = new_date.getUTCMonth(),
22133         mag = Math.abs(dir),
22134         new_month, test;
22135         dir = dir > 0 ? 1 : -1;
22136         if (mag == 1){
22137             test = dir == -1
22138             // If going back one month, make sure month is not current month
22139             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22140             ? function(){
22141                 return new_date.getUTCMonth() == month;
22142             }
22143             // If going forward one month, make sure month is as expected
22144             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22145             : function(){
22146                 return new_date.getUTCMonth() != new_month;
22147             };
22148             new_month = month + dir;
22149             new_date.setUTCMonth(new_month);
22150             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22151             if (new_month < 0 || new_month > 11) {
22152                 new_month = (new_month + 12) % 12;
22153             }
22154         } else {
22155             // For magnitudes >1, move one month at a time...
22156             for (var i=0; i<mag; i++) {
22157                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22158                 new_date = this.moveMonth(new_date, dir);
22159             }
22160             // ...then reset the day, keeping it in the new month
22161             new_month = new_date.getUTCMonth();
22162             new_date.setUTCDate(day);
22163             test = function(){
22164                 return new_month != new_date.getUTCMonth();
22165             };
22166         }
22167         // Common date-resetting loop -- if date is beyond end of month, make it
22168         // end of month
22169         while (test()){
22170             new_date.setUTCDate(--day);
22171             new_date.setUTCMonth(new_month);
22172         }
22173         return new_date;
22174     },
22175
22176     moveYear: function(date, dir)
22177     {
22178         return this.moveMonth(date, dir*12);
22179     },
22180
22181     dateWithinRange: function(date)
22182     {
22183         return date >= this.startDate && date <= this.endDate;
22184     },
22185
22186     
22187     remove: function() 
22188     {
22189         this.picker().remove();
22190     },
22191     
22192     validateValue : function(value)
22193     {
22194         if(this.getVisibilityEl().hasClass('hidden')){
22195             return true;
22196         }
22197         
22198         if(value.length < 1)  {
22199             if(this.allowBlank){
22200                 return true;
22201             }
22202             return false;
22203         }
22204         
22205         if(value.length < this.minLength){
22206             return false;
22207         }
22208         if(value.length > this.maxLength){
22209             return false;
22210         }
22211         if(this.vtype){
22212             var vt = Roo.form.VTypes;
22213             if(!vt[this.vtype](value, this)){
22214                 return false;
22215             }
22216         }
22217         if(typeof this.validator == "function"){
22218             var msg = this.validator(value);
22219             if(msg !== true){
22220                 return false;
22221             }
22222         }
22223         
22224         if(this.regex && !this.regex.test(value)){
22225             return false;
22226         }
22227         
22228         if(typeof(this.parseDate(value)) == 'undefined'){
22229             return false;
22230         }
22231         
22232         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22233             return false;
22234         }      
22235         
22236         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22237             return false;
22238         } 
22239         
22240         
22241         return true;
22242     },
22243     
22244     reset : function()
22245     {
22246         this.date = this.viewDate = '';
22247         
22248         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22249     }
22250    
22251 });
22252
22253 Roo.apply(Roo.bootstrap.DateField,  {
22254     
22255     head : {
22256         tag: 'thead',
22257         cn: [
22258         {
22259             tag: 'tr',
22260             cn: [
22261             {
22262                 tag: 'th',
22263                 cls: 'prev',
22264                 html: '<i class="fa fa-arrow-left"/>'
22265             },
22266             {
22267                 tag: 'th',
22268                 cls: 'switch',
22269                 colspan: '5'
22270             },
22271             {
22272                 tag: 'th',
22273                 cls: 'next',
22274                 html: '<i class="fa fa-arrow-right"/>'
22275             }
22276
22277             ]
22278         }
22279         ]
22280     },
22281     
22282     content : {
22283         tag: 'tbody',
22284         cn: [
22285         {
22286             tag: 'tr',
22287             cn: [
22288             {
22289                 tag: 'td',
22290                 colspan: '7'
22291             }
22292             ]
22293         }
22294         ]
22295     },
22296     
22297     footer : {
22298         tag: 'tfoot',
22299         cn: [
22300         {
22301             tag: 'tr',
22302             cn: [
22303             {
22304                 tag: 'th',
22305                 colspan: '7',
22306                 cls: 'today'
22307             }
22308                     
22309             ]
22310         }
22311         ]
22312     },
22313     
22314     dates:{
22315         en: {
22316             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22317             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22318             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22319             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22320             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22321             today: "Today"
22322         }
22323     },
22324     
22325     modes: [
22326     {
22327         clsName: 'days',
22328         navFnc: 'Month',
22329         navStep: 1
22330     },
22331     {
22332         clsName: 'months',
22333         navFnc: 'FullYear',
22334         navStep: 1
22335     },
22336     {
22337         clsName: 'years',
22338         navFnc: 'FullYear',
22339         navStep: 10
22340     }]
22341 });
22342
22343 Roo.apply(Roo.bootstrap.DateField,  {
22344   
22345     template : {
22346         tag: 'div',
22347         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22348         cn: [
22349         {
22350             tag: 'div',
22351             cls: 'datepicker-days',
22352             cn: [
22353             {
22354                 tag: 'table',
22355                 cls: 'table-condensed',
22356                 cn:[
22357                 Roo.bootstrap.DateField.head,
22358                 {
22359                     tag: 'tbody'
22360                 },
22361                 Roo.bootstrap.DateField.footer
22362                 ]
22363             }
22364             ]
22365         },
22366         {
22367             tag: 'div',
22368             cls: 'datepicker-months',
22369             cn: [
22370             {
22371                 tag: 'table',
22372                 cls: 'table-condensed',
22373                 cn:[
22374                 Roo.bootstrap.DateField.head,
22375                 Roo.bootstrap.DateField.content,
22376                 Roo.bootstrap.DateField.footer
22377                 ]
22378             }
22379             ]
22380         },
22381         {
22382             tag: 'div',
22383             cls: 'datepicker-years',
22384             cn: [
22385             {
22386                 tag: 'table',
22387                 cls: 'table-condensed',
22388                 cn:[
22389                 Roo.bootstrap.DateField.head,
22390                 Roo.bootstrap.DateField.content,
22391                 Roo.bootstrap.DateField.footer
22392                 ]
22393             }
22394             ]
22395         }
22396         ]
22397     }
22398 });
22399
22400  
22401
22402  /*
22403  * - LGPL
22404  *
22405  * TimeField
22406  * 
22407  */
22408
22409 /**
22410  * @class Roo.bootstrap.TimeField
22411  * @extends Roo.bootstrap.Input
22412  * Bootstrap DateField class
22413  * 
22414  * 
22415  * @constructor
22416  * Create a new TimeField
22417  * @param {Object} config The config object
22418  */
22419
22420 Roo.bootstrap.TimeField = function(config){
22421     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22422     this.addEvents({
22423             /**
22424              * @event show
22425              * Fires when this field show.
22426              * @param {Roo.bootstrap.DateField} thisthis
22427              * @param {Mixed} date The date value
22428              */
22429             show : true,
22430             /**
22431              * @event show
22432              * Fires when this field hide.
22433              * @param {Roo.bootstrap.DateField} this
22434              * @param {Mixed} date The date value
22435              */
22436             hide : true,
22437             /**
22438              * @event select
22439              * Fires when select a date.
22440              * @param {Roo.bootstrap.DateField} this
22441              * @param {Mixed} date The date value
22442              */
22443             select : true
22444         });
22445 };
22446
22447 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22448     
22449     /**
22450      * @cfg {String} format
22451      * The default time format string which can be overriden for localization support.  The format must be
22452      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22453      */
22454     format : "H:i",
22455
22456     getAutoCreate : function()
22457     {
22458         this.after = '<i class="fa far fa-clock"></i>';
22459         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22460         
22461          
22462     },
22463     onRender: function(ct, position)
22464     {
22465         
22466         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22467                 
22468         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22469         
22470         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22471         
22472         this.pop = this.picker().select('>.datepicker-time',true).first();
22473         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22474         
22475         this.picker().on('mousedown', this.onMousedown, this);
22476         this.picker().on('click', this.onClick, this);
22477         
22478         this.picker().addClass('datepicker-dropdown');
22479     
22480         this.fillTime();
22481         this.update();
22482             
22483         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22484         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22485         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22486         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22487         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22488         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22489
22490     },
22491     
22492     fireKey: function(e){
22493         if (!this.picker().isVisible()){
22494             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22495                 this.show();
22496             }
22497             return;
22498         }
22499
22500         e.preventDefault();
22501         
22502         switch(e.keyCode){
22503             case 27: // escape
22504                 this.hide();
22505                 break;
22506             case 37: // left
22507             case 39: // right
22508                 this.onTogglePeriod();
22509                 break;
22510             case 38: // up
22511                 this.onIncrementMinutes();
22512                 break;
22513             case 40: // down
22514                 this.onDecrementMinutes();
22515                 break;
22516             case 13: // enter
22517             case 9: // tab
22518                 this.setTime();
22519                 break;
22520         }
22521     },
22522     
22523     onClick: function(e) {
22524         e.stopPropagation();
22525         e.preventDefault();
22526     },
22527     
22528     picker : function()
22529     {
22530         return this.pickerEl;
22531     },
22532     
22533     fillTime: function()
22534     {    
22535         var time = this.pop.select('tbody', true).first();
22536         
22537         time.dom.innerHTML = '';
22538         
22539         time.createChild({
22540             tag: 'tr',
22541             cn: [
22542                 {
22543                     tag: 'td',
22544                     cn: [
22545                         {
22546                             tag: 'a',
22547                             href: '#',
22548                             cls: 'btn',
22549                             cn: [
22550                                 {
22551                                     tag: 'i',
22552                                     cls: 'hours-up fa fas fa-chevron-up'
22553                                 }
22554                             ]
22555                         } 
22556                     ]
22557                 },
22558                 {
22559                     tag: 'td',
22560                     cls: 'separator'
22561                 },
22562                 {
22563                     tag: 'td',
22564                     cn: [
22565                         {
22566                             tag: 'a',
22567                             href: '#',
22568                             cls: 'btn',
22569                             cn: [
22570                                 {
22571                                     tag: 'i',
22572                                     cls: 'minutes-up fa fas fa-chevron-up'
22573                                 }
22574                             ]
22575                         }
22576                     ]
22577                 },
22578                 {
22579                     tag: 'td',
22580                     cls: 'separator'
22581                 }
22582             ]
22583         });
22584         
22585         time.createChild({
22586             tag: 'tr',
22587             cn: [
22588                 {
22589                     tag: 'td',
22590                     cn: [
22591                         {
22592                             tag: 'span',
22593                             cls: 'timepicker-hour',
22594                             html: '00'
22595                         }  
22596                     ]
22597                 },
22598                 {
22599                     tag: 'td',
22600                     cls: 'separator',
22601                     html: ':'
22602                 },
22603                 {
22604                     tag: 'td',
22605                     cn: [
22606                         {
22607                             tag: 'span',
22608                             cls: 'timepicker-minute',
22609                             html: '00'
22610                         }  
22611                     ]
22612                 },
22613                 {
22614                     tag: 'td',
22615                     cls: 'separator'
22616                 },
22617                 {
22618                     tag: 'td',
22619                     cn: [
22620                         {
22621                             tag: 'button',
22622                             type: 'button',
22623                             cls: 'btn btn-primary period',
22624                             html: 'AM'
22625                             
22626                         }
22627                     ]
22628                 }
22629             ]
22630         });
22631         
22632         time.createChild({
22633             tag: 'tr',
22634             cn: [
22635                 {
22636                     tag: 'td',
22637                     cn: [
22638                         {
22639                             tag: 'a',
22640                             href: '#',
22641                             cls: 'btn',
22642                             cn: [
22643                                 {
22644                                     tag: 'span',
22645                                     cls: 'hours-down fa fas fa-chevron-down'
22646                                 }
22647                             ]
22648                         }
22649                     ]
22650                 },
22651                 {
22652                     tag: 'td',
22653                     cls: 'separator'
22654                 },
22655                 {
22656                     tag: 'td',
22657                     cn: [
22658                         {
22659                             tag: 'a',
22660                             href: '#',
22661                             cls: 'btn',
22662                             cn: [
22663                                 {
22664                                     tag: 'span',
22665                                     cls: 'minutes-down fa fas fa-chevron-down'
22666                                 }
22667                             ]
22668                         }
22669                     ]
22670                 },
22671                 {
22672                     tag: 'td',
22673                     cls: 'separator'
22674                 }
22675             ]
22676         });
22677         
22678     },
22679     
22680     update: function()
22681     {
22682         
22683         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22684         
22685         this.fill();
22686     },
22687     
22688     fill: function() 
22689     {
22690         var hours = this.time.getHours();
22691         var minutes = this.time.getMinutes();
22692         var period = 'AM';
22693         
22694         if(hours > 11){
22695             period = 'PM';
22696         }
22697         
22698         if(hours == 0){
22699             hours = 12;
22700         }
22701         
22702         
22703         if(hours > 12){
22704             hours = hours - 12;
22705         }
22706         
22707         if(hours < 10){
22708             hours = '0' + hours;
22709         }
22710         
22711         if(minutes < 10){
22712             minutes = '0' + minutes;
22713         }
22714         
22715         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22716         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22717         this.pop.select('button', true).first().dom.innerHTML = period;
22718         
22719     },
22720     
22721     place: function()
22722     {   
22723         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22724         
22725         var cls = ['bottom'];
22726         
22727         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22728             cls.pop();
22729             cls.push('top');
22730         }
22731         
22732         cls.push('right');
22733         
22734         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22735             cls.pop();
22736             cls.push('left');
22737         }
22738         //this.picker().setXY(20000,20000);
22739         this.picker().addClass(cls.join('-'));
22740         
22741         var _this = this;
22742         
22743         Roo.each(cls, function(c){
22744             if(c == 'bottom'){
22745                 (function() {
22746                  //  
22747                 }).defer(200);
22748                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22749                 //_this.picker().setTop(_this.inputEl().getHeight());
22750                 return;
22751             }
22752             if(c == 'top'){
22753                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22754                 
22755                 //_this.picker().setTop(0 - _this.picker().getHeight());
22756                 return;
22757             }
22758             /*
22759             if(c == 'left'){
22760                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22761                 return;
22762             }
22763             if(c == 'right'){
22764                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22765                 return;
22766             }
22767             */
22768         });
22769         
22770     },
22771   
22772     onFocus : function()
22773     {
22774         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22775         this.show();
22776     },
22777     
22778     onBlur : function()
22779     {
22780         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22781         this.hide();
22782     },
22783     
22784     show : function()
22785     {
22786         this.picker().show();
22787         this.pop.show();
22788         this.update();
22789         this.place();
22790         
22791         this.fireEvent('show', this, this.date);
22792     },
22793     
22794     hide : function()
22795     {
22796         this.picker().hide();
22797         this.pop.hide();
22798         
22799         this.fireEvent('hide', this, this.date);
22800     },
22801     
22802     setTime : function()
22803     {
22804         this.hide();
22805         this.setValue(this.time.format(this.format));
22806         
22807         this.fireEvent('select', this, this.date);
22808         
22809         
22810     },
22811     
22812     onMousedown: function(e){
22813         e.stopPropagation();
22814         e.preventDefault();
22815     },
22816     
22817     onIncrementHours: function()
22818     {
22819         Roo.log('onIncrementHours');
22820         this.time = this.time.add(Date.HOUR, 1);
22821         this.update();
22822         
22823     },
22824     
22825     onDecrementHours: function()
22826     {
22827         Roo.log('onDecrementHours');
22828         this.time = this.time.add(Date.HOUR, -1);
22829         this.update();
22830     },
22831     
22832     onIncrementMinutes: function()
22833     {
22834         Roo.log('onIncrementMinutes');
22835         this.time = this.time.add(Date.MINUTE, 1);
22836         this.update();
22837     },
22838     
22839     onDecrementMinutes: function()
22840     {
22841         Roo.log('onDecrementMinutes');
22842         this.time = this.time.add(Date.MINUTE, -1);
22843         this.update();
22844     },
22845     
22846     onTogglePeriod: function()
22847     {
22848         Roo.log('onTogglePeriod');
22849         this.time = this.time.add(Date.HOUR, 12);
22850         this.update();
22851     }
22852     
22853    
22854 });
22855  
22856
22857 Roo.apply(Roo.bootstrap.TimeField,  {
22858   
22859     template : {
22860         tag: 'div',
22861         cls: 'datepicker dropdown-menu',
22862         cn: [
22863             {
22864                 tag: 'div',
22865                 cls: 'datepicker-time',
22866                 cn: [
22867                 {
22868                     tag: 'table',
22869                     cls: 'table-condensed',
22870                     cn:[
22871                         {
22872                             tag: 'tbody',
22873                             cn: [
22874                                 {
22875                                     tag: 'tr',
22876                                     cn: [
22877                                     {
22878                                         tag: 'td',
22879                                         colspan: '7'
22880                                     }
22881                                     ]
22882                                 }
22883                             ]
22884                         },
22885                         {
22886                             tag: 'tfoot',
22887                             cn: [
22888                                 {
22889                                     tag: 'tr',
22890                                     cn: [
22891                                     {
22892                                         tag: 'th',
22893                                         colspan: '7',
22894                                         cls: '',
22895                                         cn: [
22896                                             {
22897                                                 tag: 'button',
22898                                                 cls: 'btn btn-info ok',
22899                                                 html: 'OK'
22900                                             }
22901                                         ]
22902                                     }
22903                     
22904                                     ]
22905                                 }
22906                             ]
22907                         }
22908                     ]
22909                 }
22910                 ]
22911             }
22912         ]
22913     }
22914 });
22915
22916  
22917
22918  /*
22919  * - LGPL
22920  *
22921  * MonthField
22922  * 
22923  */
22924
22925 /**
22926  * @class Roo.bootstrap.MonthField
22927  * @extends Roo.bootstrap.Input
22928  * Bootstrap MonthField class
22929  * 
22930  * @cfg {String} language default en
22931  * 
22932  * @constructor
22933  * Create a new MonthField
22934  * @param {Object} config The config object
22935  */
22936
22937 Roo.bootstrap.MonthField = function(config){
22938     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22939     
22940     this.addEvents({
22941         /**
22942          * @event show
22943          * Fires when this field show.
22944          * @param {Roo.bootstrap.MonthField} this
22945          * @param {Mixed} date The date value
22946          */
22947         show : true,
22948         /**
22949          * @event show
22950          * Fires when this field hide.
22951          * @param {Roo.bootstrap.MonthField} this
22952          * @param {Mixed} date The date value
22953          */
22954         hide : true,
22955         /**
22956          * @event select
22957          * Fires when select a date.
22958          * @param {Roo.bootstrap.MonthField} this
22959          * @param {String} oldvalue The old value
22960          * @param {String} newvalue The new value
22961          */
22962         select : true
22963     });
22964 };
22965
22966 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22967     
22968     onRender: function(ct, position)
22969     {
22970         
22971         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22972         
22973         this.language = this.language || 'en';
22974         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22975         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22976         
22977         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22978         this.isInline = false;
22979         this.isInput = true;
22980         this.component = this.el.select('.add-on', true).first() || false;
22981         this.component = (this.component && this.component.length === 0) ? false : this.component;
22982         this.hasInput = this.component && this.inputEL().length;
22983         
22984         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22985         
22986         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22987         
22988         this.picker().on('mousedown', this.onMousedown, this);
22989         this.picker().on('click', this.onClick, this);
22990         
22991         this.picker().addClass('datepicker-dropdown');
22992         
22993         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22994             v.setStyle('width', '189px');
22995         });
22996         
22997         this.fillMonths();
22998         
22999         this.update();
23000         
23001         if(this.isInline) {
23002             this.show();
23003         }
23004         
23005     },
23006     
23007     setValue: function(v, suppressEvent)
23008     {   
23009         var o = this.getValue();
23010         
23011         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23012         
23013         this.update();
23014
23015         if(suppressEvent !== true){
23016             this.fireEvent('select', this, o, v);
23017         }
23018         
23019     },
23020     
23021     getValue: function()
23022     {
23023         return this.value;
23024     },
23025     
23026     onClick: function(e) 
23027     {
23028         e.stopPropagation();
23029         e.preventDefault();
23030         
23031         var target = e.getTarget();
23032         
23033         if(target.nodeName.toLowerCase() === 'i'){
23034             target = Roo.get(target).dom.parentNode;
23035         }
23036         
23037         var nodeName = target.nodeName;
23038         var className = target.className;
23039         var html = target.innerHTML;
23040         
23041         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23042             return;
23043         }
23044         
23045         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23046         
23047         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23048         
23049         this.hide();
23050                         
23051     },
23052     
23053     picker : function()
23054     {
23055         return this.pickerEl;
23056     },
23057     
23058     fillMonths: function()
23059     {    
23060         var i = 0;
23061         var months = this.picker().select('>.datepicker-months td', true).first();
23062         
23063         months.dom.innerHTML = '';
23064         
23065         while (i < 12) {
23066             var month = {
23067                 tag: 'span',
23068                 cls: 'month',
23069                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23070             };
23071             
23072             months.createChild(month);
23073         }
23074         
23075     },
23076     
23077     update: function()
23078     {
23079         var _this = this;
23080         
23081         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23082             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23083         }
23084         
23085         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23086             e.removeClass('active');
23087             
23088             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23089                 e.addClass('active');
23090             }
23091         })
23092     },
23093     
23094     place: function()
23095     {
23096         if(this.isInline) {
23097             return;
23098         }
23099         
23100         this.picker().removeClass(['bottom', 'top']);
23101         
23102         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23103             /*
23104              * place to the top of element!
23105              *
23106              */
23107             
23108             this.picker().addClass('top');
23109             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23110             
23111             return;
23112         }
23113         
23114         this.picker().addClass('bottom');
23115         
23116         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23117     },
23118     
23119     onFocus : function()
23120     {
23121         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23122         this.show();
23123     },
23124     
23125     onBlur : function()
23126     {
23127         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23128         
23129         var d = this.inputEl().getValue();
23130         
23131         this.setValue(d);
23132                 
23133         this.hide();
23134     },
23135     
23136     show : function()
23137     {
23138         this.picker().show();
23139         this.picker().select('>.datepicker-months', true).first().show();
23140         this.update();
23141         this.place();
23142         
23143         this.fireEvent('show', this, this.date);
23144     },
23145     
23146     hide : function()
23147     {
23148         if(this.isInline) {
23149             return;
23150         }
23151         this.picker().hide();
23152         this.fireEvent('hide', this, this.date);
23153         
23154     },
23155     
23156     onMousedown: function(e)
23157     {
23158         e.stopPropagation();
23159         e.preventDefault();
23160     },
23161     
23162     keyup: function(e)
23163     {
23164         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23165         this.update();
23166     },
23167
23168     fireKey: function(e)
23169     {
23170         if (!this.picker().isVisible()){
23171             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23172                 this.show();
23173             }
23174             return;
23175         }
23176         
23177         var dir;
23178         
23179         switch(e.keyCode){
23180             case 27: // escape
23181                 this.hide();
23182                 e.preventDefault();
23183                 break;
23184             case 37: // left
23185             case 39: // right
23186                 dir = e.keyCode == 37 ? -1 : 1;
23187                 
23188                 this.vIndex = this.vIndex + dir;
23189                 
23190                 if(this.vIndex < 0){
23191                     this.vIndex = 0;
23192                 }
23193                 
23194                 if(this.vIndex > 11){
23195                     this.vIndex = 11;
23196                 }
23197                 
23198                 if(isNaN(this.vIndex)){
23199                     this.vIndex = 0;
23200                 }
23201                 
23202                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23203                 
23204                 break;
23205             case 38: // up
23206             case 40: // down
23207                 
23208                 dir = e.keyCode == 38 ? -1 : 1;
23209                 
23210                 this.vIndex = this.vIndex + dir * 4;
23211                 
23212                 if(this.vIndex < 0){
23213                     this.vIndex = 0;
23214                 }
23215                 
23216                 if(this.vIndex > 11){
23217                     this.vIndex = 11;
23218                 }
23219                 
23220                 if(isNaN(this.vIndex)){
23221                     this.vIndex = 0;
23222                 }
23223                 
23224                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23225                 break;
23226                 
23227             case 13: // enter
23228                 
23229                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23230                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23231                 }
23232                 
23233                 this.hide();
23234                 e.preventDefault();
23235                 break;
23236             case 9: // tab
23237                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23238                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23239                 }
23240                 this.hide();
23241                 break;
23242             case 16: // shift
23243             case 17: // ctrl
23244             case 18: // alt
23245                 break;
23246             default :
23247                 this.hide();
23248                 
23249         }
23250     },
23251     
23252     remove: function() 
23253     {
23254         this.picker().remove();
23255     }
23256    
23257 });
23258
23259 Roo.apply(Roo.bootstrap.MonthField,  {
23260     
23261     content : {
23262         tag: 'tbody',
23263         cn: [
23264         {
23265             tag: 'tr',
23266             cn: [
23267             {
23268                 tag: 'td',
23269                 colspan: '7'
23270             }
23271             ]
23272         }
23273         ]
23274     },
23275     
23276     dates:{
23277         en: {
23278             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23279             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23280         }
23281     }
23282 });
23283
23284 Roo.apply(Roo.bootstrap.MonthField,  {
23285   
23286     template : {
23287         tag: 'div',
23288         cls: 'datepicker dropdown-menu roo-dynamic',
23289         cn: [
23290             {
23291                 tag: 'div',
23292                 cls: 'datepicker-months',
23293                 cn: [
23294                 {
23295                     tag: 'table',
23296                     cls: 'table-condensed',
23297                     cn:[
23298                         Roo.bootstrap.DateField.content
23299                     ]
23300                 }
23301                 ]
23302             }
23303         ]
23304     }
23305 });
23306
23307  
23308
23309  
23310  /*
23311  * - LGPL
23312  *
23313  * CheckBox
23314  * 
23315  */
23316
23317 /**
23318  * @class Roo.bootstrap.CheckBox
23319  * @extends Roo.bootstrap.Input
23320  * Bootstrap CheckBox class
23321  * 
23322  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23323  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23324  * @cfg {String} boxLabel The text that appears beside the checkbox
23325  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23326  * @cfg {Boolean} checked initnal the element
23327  * @cfg {Boolean} inline inline the element (default false)
23328  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23329  * @cfg {String} tooltip label tooltip
23330  * 
23331  * @constructor
23332  * Create a new CheckBox
23333  * @param {Object} config The config object
23334  */
23335
23336 Roo.bootstrap.CheckBox = function(config){
23337     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23338    
23339     this.addEvents({
23340         /**
23341         * @event check
23342         * Fires when the element is checked or unchecked.
23343         * @param {Roo.bootstrap.CheckBox} this This input
23344         * @param {Boolean} checked The new checked value
23345         */
23346        check : true,
23347        /**
23348         * @event click
23349         * Fires when the element is click.
23350         * @param {Roo.bootstrap.CheckBox} this This input
23351         */
23352        click : true
23353     });
23354     
23355 };
23356
23357 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23358   
23359     inputType: 'checkbox',
23360     inputValue: 1,
23361     valueOff: 0,
23362     boxLabel: false,
23363     checked: false,
23364     weight : false,
23365     inline: false,
23366     tooltip : '',
23367     
23368     // checkbox success does not make any sense really.. 
23369     invalidClass : "",
23370     validClass : "",
23371     
23372     
23373     getAutoCreate : function()
23374     {
23375         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23376         
23377         var id = Roo.id();
23378         
23379         var cfg = {};
23380         
23381         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23382         
23383         if(this.inline){
23384             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23385         }
23386         
23387         var input =  {
23388             tag: 'input',
23389             id : id,
23390             type : this.inputType,
23391             value : this.inputValue,
23392             cls : 'roo-' + this.inputType, //'form-box',
23393             placeholder : this.placeholder || ''
23394             
23395         };
23396         
23397         if(this.inputType != 'radio'){
23398             var hidden =  {
23399                 tag: 'input',
23400                 type : 'hidden',
23401                 cls : 'roo-hidden-value',
23402                 value : this.checked ? this.inputValue : this.valueOff
23403             };
23404         }
23405         
23406             
23407         if (this.weight) { // Validity check?
23408             cfg.cls += " " + this.inputType + "-" + this.weight;
23409         }
23410         
23411         if (this.disabled) {
23412             input.disabled=true;
23413         }
23414         
23415         if(this.checked){
23416             input.checked = this.checked;
23417         }
23418         
23419         if (this.name) {
23420             
23421             input.name = this.name;
23422             
23423             if(this.inputType != 'radio'){
23424                 hidden.name = this.name;
23425                 input.name = '_hidden_' + this.name;
23426             }
23427         }
23428         
23429         if (this.size) {
23430             input.cls += ' input-' + this.size;
23431         }
23432         
23433         var settings=this;
23434         
23435         ['xs','sm','md','lg'].map(function(size){
23436             if (settings[size]) {
23437                 cfg.cls += ' col-' + size + '-' + settings[size];
23438             }
23439         });
23440         
23441         var inputblock = input;
23442          
23443         if (this.before || this.after) {
23444             
23445             inputblock = {
23446                 cls : 'input-group',
23447                 cn :  [] 
23448             };
23449             
23450             if (this.before) {
23451                 inputblock.cn.push({
23452                     tag :'span',
23453                     cls : 'input-group-addon',
23454                     html : this.before
23455                 });
23456             }
23457             
23458             inputblock.cn.push(input);
23459             
23460             if(this.inputType != 'radio'){
23461                 inputblock.cn.push(hidden);
23462             }
23463             
23464             if (this.after) {
23465                 inputblock.cn.push({
23466                     tag :'span',
23467                     cls : 'input-group-addon',
23468                     html : this.after
23469                 });
23470             }
23471             
23472         }
23473         var boxLabelCfg = false;
23474         
23475         if(this.boxLabel){
23476            
23477             boxLabelCfg = {
23478                 tag: 'label',
23479                 //'for': id, // box label is handled by onclick - so no for...
23480                 cls: 'box-label',
23481                 html: this.boxLabel
23482             };
23483             if(this.tooltip){
23484                 boxLabelCfg.tooltip = this.tooltip;
23485             }
23486              
23487         }
23488         
23489         
23490         if (align ==='left' && this.fieldLabel.length) {
23491 //                Roo.log("left and has label");
23492             cfg.cn = [
23493                 {
23494                     tag: 'label',
23495                     'for' :  id,
23496                     cls : 'control-label',
23497                     html : this.fieldLabel
23498                 },
23499                 {
23500                     cls : "", 
23501                     cn: [
23502                         inputblock
23503                     ]
23504                 }
23505             ];
23506             
23507             if (boxLabelCfg) {
23508                 cfg.cn[1].cn.push(boxLabelCfg);
23509             }
23510             
23511             if(this.labelWidth > 12){
23512                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23513             }
23514             
23515             if(this.labelWidth < 13 && this.labelmd == 0){
23516                 this.labelmd = this.labelWidth;
23517             }
23518             
23519             if(this.labellg > 0){
23520                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23521                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23522             }
23523             
23524             if(this.labelmd > 0){
23525                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23526                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23527             }
23528             
23529             if(this.labelsm > 0){
23530                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23531                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23532             }
23533             
23534             if(this.labelxs > 0){
23535                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23536                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23537             }
23538             
23539         } else if ( this.fieldLabel.length) {
23540 //                Roo.log(" label");
23541                 cfg.cn = [
23542                    
23543                     {
23544                         tag: this.boxLabel ? 'span' : 'label',
23545                         'for': id,
23546                         cls: 'control-label box-input-label',
23547                         //cls : 'input-group-addon',
23548                         html : this.fieldLabel
23549                     },
23550                     
23551                     inputblock
23552                     
23553                 ];
23554                 if (boxLabelCfg) {
23555                     cfg.cn.push(boxLabelCfg);
23556                 }
23557
23558         } else {
23559             
23560 //                Roo.log(" no label && no align");
23561                 cfg.cn = [  inputblock ] ;
23562                 if (boxLabelCfg) {
23563                     cfg.cn.push(boxLabelCfg);
23564                 }
23565
23566                 
23567         }
23568         
23569        
23570         
23571         if(this.inputType != 'radio'){
23572             cfg.cn.push(hidden);
23573         }
23574         
23575         return cfg;
23576         
23577     },
23578     
23579     /**
23580      * return the real input element.
23581      */
23582     inputEl: function ()
23583     {
23584         return this.el.select('input.roo-' + this.inputType,true).first();
23585     },
23586     hiddenEl: function ()
23587     {
23588         return this.el.select('input.roo-hidden-value',true).first();
23589     },
23590     
23591     labelEl: function()
23592     {
23593         return this.el.select('label.control-label',true).first();
23594     },
23595     /* depricated... */
23596     
23597     label: function()
23598     {
23599         return this.labelEl();
23600     },
23601     
23602     boxLabelEl: function()
23603     {
23604         return this.el.select('label.box-label',true).first();
23605     },
23606     
23607     initEvents : function()
23608     {
23609 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23610         
23611         this.inputEl().on('click', this.onClick,  this);
23612         
23613         if (this.boxLabel) { 
23614             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23615         }
23616         
23617         this.startValue = this.getValue();
23618         
23619         if(this.groupId){
23620             Roo.bootstrap.CheckBox.register(this);
23621         }
23622     },
23623     
23624     onClick : function(e)
23625     {   
23626         if(this.fireEvent('click', this, e) !== false){
23627             this.setChecked(!this.checked);
23628         }
23629         
23630     },
23631     
23632     setChecked : function(state,suppressEvent)
23633     {
23634         this.startValue = this.getValue();
23635
23636         if(this.inputType == 'radio'){
23637             
23638             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23639                 e.dom.checked = false;
23640             });
23641             
23642             this.inputEl().dom.checked = true;
23643             
23644             this.inputEl().dom.value = this.inputValue;
23645             
23646             if(suppressEvent !== true){
23647                 this.fireEvent('check', this, true);
23648             }
23649             
23650             this.validate();
23651             
23652             return;
23653         }
23654         
23655         this.checked = state;
23656         
23657         this.inputEl().dom.checked = state;
23658         
23659         
23660         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23661         
23662         if(suppressEvent !== true){
23663             this.fireEvent('check', this, state);
23664         }
23665         
23666         this.validate();
23667     },
23668     
23669     getValue : function()
23670     {
23671         if(this.inputType == 'radio'){
23672             return this.getGroupValue();
23673         }
23674         
23675         return this.hiddenEl().dom.value;
23676         
23677     },
23678     
23679     getGroupValue : function()
23680     {
23681         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23682             return '';
23683         }
23684         
23685         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23686     },
23687     
23688     setValue : function(v,suppressEvent)
23689     {
23690         if(this.inputType == 'radio'){
23691             this.setGroupValue(v, suppressEvent);
23692             return;
23693         }
23694         
23695         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23696         
23697         this.validate();
23698     },
23699     
23700     setGroupValue : function(v, suppressEvent)
23701     {
23702         this.startValue = this.getValue();
23703         
23704         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23705             e.dom.checked = false;
23706             
23707             if(e.dom.value == v){
23708                 e.dom.checked = true;
23709             }
23710         });
23711         
23712         if(suppressEvent !== true){
23713             this.fireEvent('check', this, true);
23714         }
23715
23716         this.validate();
23717         
23718         return;
23719     },
23720     
23721     validate : function()
23722     {
23723         if(this.getVisibilityEl().hasClass('hidden')){
23724             return true;
23725         }
23726         
23727         if(
23728                 this.disabled || 
23729                 (this.inputType == 'radio' && this.validateRadio()) ||
23730                 (this.inputType == 'checkbox' && this.validateCheckbox())
23731         ){
23732             this.markValid();
23733             return true;
23734         }
23735         
23736         this.markInvalid();
23737         return false;
23738     },
23739     
23740     validateRadio : function()
23741     {
23742         if(this.getVisibilityEl().hasClass('hidden')){
23743             return true;
23744         }
23745         
23746         if(this.allowBlank){
23747             return true;
23748         }
23749         
23750         var valid = false;
23751         
23752         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23753             if(!e.dom.checked){
23754                 return;
23755             }
23756             
23757             valid = true;
23758             
23759             return false;
23760         });
23761         
23762         return valid;
23763     },
23764     
23765     validateCheckbox : function()
23766     {
23767         if(!this.groupId){
23768             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23769             //return (this.getValue() == this.inputValue) ? true : false;
23770         }
23771         
23772         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23773         
23774         if(!group){
23775             return false;
23776         }
23777         
23778         var r = false;
23779         
23780         for(var i in group){
23781             if(group[i].el.isVisible(true)){
23782                 r = false;
23783                 break;
23784             }
23785             
23786             r = true;
23787         }
23788         
23789         for(var i in group){
23790             if(r){
23791                 break;
23792             }
23793             
23794             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23795         }
23796         
23797         return r;
23798     },
23799     
23800     /**
23801      * Mark this field as valid
23802      */
23803     markValid : function()
23804     {
23805         var _this = this;
23806         
23807         this.fireEvent('valid', this);
23808         
23809         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23810         
23811         if(this.groupId){
23812             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23813         }
23814         
23815         if(label){
23816             label.markValid();
23817         }
23818
23819         if(this.inputType == 'radio'){
23820             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23821                 var fg = e.findParent('.form-group', false, true);
23822                 if (Roo.bootstrap.version == 3) {
23823                     fg.removeClass([_this.invalidClass, _this.validClass]);
23824                     fg.addClass(_this.validClass);
23825                 } else {
23826                     fg.removeClass(['is-valid', 'is-invalid']);
23827                     fg.addClass('is-valid');
23828                 }
23829             });
23830             
23831             return;
23832         }
23833
23834         if(!this.groupId){
23835             var fg = this.el.findParent('.form-group', false, true);
23836             if (Roo.bootstrap.version == 3) {
23837                 fg.removeClass([this.invalidClass, this.validClass]);
23838                 fg.addClass(this.validClass);
23839             } else {
23840                 fg.removeClass(['is-valid', 'is-invalid']);
23841                 fg.addClass('is-valid');
23842             }
23843             return;
23844         }
23845         
23846         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23847         
23848         if(!group){
23849             return;
23850         }
23851         
23852         for(var i in group){
23853             var fg = group[i].el.findParent('.form-group', false, true);
23854             if (Roo.bootstrap.version == 3) {
23855                 fg.removeClass([this.invalidClass, this.validClass]);
23856                 fg.addClass(this.validClass);
23857             } else {
23858                 fg.removeClass(['is-valid', 'is-invalid']);
23859                 fg.addClass('is-valid');
23860             }
23861         }
23862     },
23863     
23864      /**
23865      * Mark this field as invalid
23866      * @param {String} msg The validation message
23867      */
23868     markInvalid : function(msg)
23869     {
23870         if(this.allowBlank){
23871             return;
23872         }
23873         
23874         var _this = this;
23875         
23876         this.fireEvent('invalid', this, msg);
23877         
23878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23879         
23880         if(this.groupId){
23881             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23882         }
23883         
23884         if(label){
23885             label.markInvalid();
23886         }
23887             
23888         if(this.inputType == 'radio'){
23889             
23890             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23891                 var fg = e.findParent('.form-group', false, true);
23892                 if (Roo.bootstrap.version == 3) {
23893                     fg.removeClass([_this.invalidClass, _this.validClass]);
23894                     fg.addClass(_this.invalidClass);
23895                 } else {
23896                     fg.removeClass(['is-invalid', 'is-valid']);
23897                     fg.addClass('is-invalid');
23898                 }
23899             });
23900             
23901             return;
23902         }
23903         
23904         if(!this.groupId){
23905             var fg = this.el.findParent('.form-group', false, true);
23906             if (Roo.bootstrap.version == 3) {
23907                 fg.removeClass([_this.invalidClass, _this.validClass]);
23908                 fg.addClass(_this.invalidClass);
23909             } else {
23910                 fg.removeClass(['is-invalid', 'is-valid']);
23911                 fg.addClass('is-invalid');
23912             }
23913             return;
23914         }
23915         
23916         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23917         
23918         if(!group){
23919             return;
23920         }
23921         
23922         for(var i in group){
23923             var fg = group[i].el.findParent('.form-group', false, true);
23924             if (Roo.bootstrap.version == 3) {
23925                 fg.removeClass([_this.invalidClass, _this.validClass]);
23926                 fg.addClass(_this.invalidClass);
23927             } else {
23928                 fg.removeClass(['is-invalid', 'is-valid']);
23929                 fg.addClass('is-invalid');
23930             }
23931         }
23932         
23933     },
23934     
23935     clearInvalid : function()
23936     {
23937         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23938         
23939         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23940         
23941         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23942         
23943         if (label && label.iconEl) {
23944             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23945             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23946         }
23947     },
23948     
23949     disable : function()
23950     {
23951         if(this.inputType != 'radio'){
23952             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23953             return;
23954         }
23955         
23956         var _this = this;
23957         
23958         if(this.rendered){
23959             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23960                 _this.getActionEl().addClass(this.disabledClass);
23961                 e.dom.disabled = true;
23962             });
23963         }
23964         
23965         this.disabled = true;
23966         this.fireEvent("disable", this);
23967         return this;
23968     },
23969
23970     enable : function()
23971     {
23972         if(this.inputType != 'radio'){
23973             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23974             return;
23975         }
23976         
23977         var _this = this;
23978         
23979         if(this.rendered){
23980             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23981                 _this.getActionEl().removeClass(this.disabledClass);
23982                 e.dom.disabled = false;
23983             });
23984         }
23985         
23986         this.disabled = false;
23987         this.fireEvent("enable", this);
23988         return this;
23989     },
23990     
23991     setBoxLabel : function(v)
23992     {
23993         this.boxLabel = v;
23994         
23995         if(this.rendered){
23996             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23997         }
23998     }
23999
24000 });
24001
24002 Roo.apply(Roo.bootstrap.CheckBox, {
24003     
24004     groups: {},
24005     
24006      /**
24007     * register a CheckBox Group
24008     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24009     */
24010     register : function(checkbox)
24011     {
24012         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24013             this.groups[checkbox.groupId] = {};
24014         }
24015         
24016         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24017             return;
24018         }
24019         
24020         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24021         
24022     },
24023     /**
24024     * fetch a CheckBox Group based on the group ID
24025     * @param {string} the group ID
24026     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24027     */
24028     get: function(groupId) {
24029         if (typeof(this.groups[groupId]) == 'undefined') {
24030             return false;
24031         }
24032         
24033         return this.groups[groupId] ;
24034     }
24035     
24036     
24037 });
24038 /*
24039  * - LGPL
24040  *
24041  * RadioItem
24042  * 
24043  */
24044
24045 /**
24046  * @class Roo.bootstrap.Radio
24047  * @extends Roo.bootstrap.Component
24048  * Bootstrap Radio class
24049  * @cfg {String} boxLabel - the label associated
24050  * @cfg {String} value - the value of radio
24051  * 
24052  * @constructor
24053  * Create a new Radio
24054  * @param {Object} config The config object
24055  */
24056 Roo.bootstrap.Radio = function(config){
24057     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24058     
24059 };
24060
24061 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24062     
24063     boxLabel : '',
24064     
24065     value : '',
24066     
24067     getAutoCreate : function()
24068     {
24069         var cfg = {
24070             tag : 'div',
24071             cls : 'form-group radio',
24072             cn : [
24073                 {
24074                     tag : 'label',
24075                     cls : 'box-label',
24076                     html : this.boxLabel
24077                 }
24078             ]
24079         };
24080         
24081         return cfg;
24082     },
24083     
24084     initEvents : function() 
24085     {
24086         this.parent().register(this);
24087         
24088         this.el.on('click', this.onClick, this);
24089         
24090     },
24091     
24092     onClick : function(e)
24093     {
24094         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24095             this.setChecked(true);
24096         }
24097     },
24098     
24099     setChecked : function(state, suppressEvent)
24100     {
24101         this.parent().setValue(this.value, suppressEvent);
24102         
24103     },
24104     
24105     setBoxLabel : function(v)
24106     {
24107         this.boxLabel = v;
24108         
24109         if(this.rendered){
24110             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24111         }
24112     }
24113     
24114 });
24115  
24116
24117  /*
24118  * - LGPL
24119  *
24120  * Input
24121  * 
24122  */
24123
24124 /**
24125  * @class Roo.bootstrap.SecurePass
24126  * @extends Roo.bootstrap.Input
24127  * Bootstrap SecurePass class
24128  *
24129  * 
24130  * @constructor
24131  * Create a new SecurePass
24132  * @param {Object} config The config object
24133  */
24134  
24135 Roo.bootstrap.SecurePass = function (config) {
24136     // these go here, so the translation tool can replace them..
24137     this.errors = {
24138         PwdEmpty: "Please type a password, and then retype it to confirm.",
24139         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24140         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24141         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24142         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24143         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24144         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24145         TooWeak: "Your password is Too Weak."
24146     },
24147     this.meterLabel = "Password strength:";
24148     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24149     this.meterClass = [
24150         "roo-password-meter-tooweak", 
24151         "roo-password-meter-weak", 
24152         "roo-password-meter-medium", 
24153         "roo-password-meter-strong", 
24154         "roo-password-meter-grey"
24155     ];
24156     
24157     this.errors = {};
24158     
24159     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24160 }
24161
24162 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24163     /**
24164      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24165      * {
24166      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24167      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24168      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24169      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24170      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24171      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24172      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24173      * })
24174      */
24175     // private
24176     
24177     meterWidth: 300,
24178     errorMsg :'',    
24179     errors: false,
24180     imageRoot: '/',
24181     /**
24182      * @cfg {String/Object} Label for the strength meter (defaults to
24183      * 'Password strength:')
24184      */
24185     // private
24186     meterLabel: '',
24187     /**
24188      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24189      * ['Weak', 'Medium', 'Strong'])
24190      */
24191     // private    
24192     pwdStrengths: false,    
24193     // private
24194     strength: 0,
24195     // private
24196     _lastPwd: null,
24197     // private
24198     kCapitalLetter: 0,
24199     kSmallLetter: 1,
24200     kDigit: 2,
24201     kPunctuation: 3,
24202     
24203     insecure: false,
24204     // private
24205     initEvents: function ()
24206     {
24207         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24208
24209         if (this.el.is('input[type=password]') && Roo.isSafari) {
24210             this.el.on('keydown', this.SafariOnKeyDown, this);
24211         }
24212
24213         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24214     },
24215     // private
24216     onRender: function (ct, position)
24217     {
24218         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24219         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24220         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24221
24222         this.trigger.createChild({
24223                    cn: [
24224                     {
24225                     //id: 'PwdMeter',
24226                     tag: 'div',
24227                     cls: 'roo-password-meter-grey col-xs-12',
24228                     style: {
24229                         //width: 0,
24230                         //width: this.meterWidth + 'px'                                                
24231                         }
24232                     },
24233                     {                            
24234                          cls: 'roo-password-meter-text'                          
24235                     }
24236                 ]            
24237         });
24238
24239          
24240         if (this.hideTrigger) {
24241             this.trigger.setDisplayed(false);
24242         }
24243         this.setSize(this.width || '', this.height || '');
24244     },
24245     // private
24246     onDestroy: function ()
24247     {
24248         if (this.trigger) {
24249             this.trigger.removeAllListeners();
24250             this.trigger.remove();
24251         }
24252         if (this.wrap) {
24253             this.wrap.remove();
24254         }
24255         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24256     },
24257     // private
24258     checkStrength: function ()
24259     {
24260         var pwd = this.inputEl().getValue();
24261         if (pwd == this._lastPwd) {
24262             return;
24263         }
24264
24265         var strength;
24266         if (this.ClientSideStrongPassword(pwd)) {
24267             strength = 3;
24268         } else if (this.ClientSideMediumPassword(pwd)) {
24269             strength = 2;
24270         } else if (this.ClientSideWeakPassword(pwd)) {
24271             strength = 1;
24272         } else {
24273             strength = 0;
24274         }
24275         
24276         Roo.log('strength1: ' + strength);
24277         
24278         //var pm = this.trigger.child('div/div/div').dom;
24279         var pm = this.trigger.child('div/div');
24280         pm.removeClass(this.meterClass);
24281         pm.addClass(this.meterClass[strength]);
24282                 
24283         
24284         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24285                 
24286         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24287         
24288         this._lastPwd = pwd;
24289     },
24290     reset: function ()
24291     {
24292         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24293         
24294         this._lastPwd = '';
24295         
24296         var pm = this.trigger.child('div/div');
24297         pm.removeClass(this.meterClass);
24298         pm.addClass('roo-password-meter-grey');        
24299         
24300         
24301         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24302         
24303         pt.innerHTML = '';
24304         this.inputEl().dom.type='password';
24305     },
24306     // private
24307     validateValue: function (value)
24308     {
24309         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24310             return false;
24311         }
24312         if (value.length == 0) {
24313             if (this.allowBlank) {
24314                 this.clearInvalid();
24315                 return true;
24316             }
24317
24318             this.markInvalid(this.errors.PwdEmpty);
24319             this.errorMsg = this.errors.PwdEmpty;
24320             return false;
24321         }
24322         
24323         if(this.insecure){
24324             return true;
24325         }
24326         
24327         if (!value.match(/[\x21-\x7e]+/)) {
24328             this.markInvalid(this.errors.PwdBadChar);
24329             this.errorMsg = this.errors.PwdBadChar;
24330             return false;
24331         }
24332         if (value.length < 6) {
24333             this.markInvalid(this.errors.PwdShort);
24334             this.errorMsg = this.errors.PwdShort;
24335             return false;
24336         }
24337         if (value.length > 16) {
24338             this.markInvalid(this.errors.PwdLong);
24339             this.errorMsg = this.errors.PwdLong;
24340             return false;
24341         }
24342         var strength;
24343         if (this.ClientSideStrongPassword(value)) {
24344             strength = 3;
24345         } else if (this.ClientSideMediumPassword(value)) {
24346             strength = 2;
24347         } else if (this.ClientSideWeakPassword(value)) {
24348             strength = 1;
24349         } else {
24350             strength = 0;
24351         }
24352
24353         
24354         if (strength < 2) {
24355             //this.markInvalid(this.errors.TooWeak);
24356             this.errorMsg = this.errors.TooWeak;
24357             //return false;
24358         }
24359         
24360         
24361         console.log('strength2: ' + strength);
24362         
24363         //var pm = this.trigger.child('div/div/div').dom;
24364         
24365         var pm = this.trigger.child('div/div');
24366         pm.removeClass(this.meterClass);
24367         pm.addClass(this.meterClass[strength]);
24368                 
24369         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24370                 
24371         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24372         
24373         this.errorMsg = ''; 
24374         return true;
24375     },
24376     // private
24377     CharacterSetChecks: function (type)
24378     {
24379         this.type = type;
24380         this.fResult = false;
24381     },
24382     // private
24383     isctype: function (character, type)
24384     {
24385         switch (type) {  
24386             case this.kCapitalLetter:
24387                 if (character >= 'A' && character <= 'Z') {
24388                     return true;
24389                 }
24390                 break;
24391             
24392             case this.kSmallLetter:
24393                 if (character >= 'a' && character <= 'z') {
24394                     return true;
24395                 }
24396                 break;
24397             
24398             case this.kDigit:
24399                 if (character >= '0' && character <= '9') {
24400                     return true;
24401                 }
24402                 break;
24403             
24404             case this.kPunctuation:
24405                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24406                     return true;
24407                 }
24408                 break;
24409             
24410             default:
24411                 return false;
24412         }
24413
24414     },
24415     // private
24416     IsLongEnough: function (pwd, size)
24417     {
24418         return !(pwd == null || isNaN(size) || pwd.length < size);
24419     },
24420     // private
24421     SpansEnoughCharacterSets: function (word, nb)
24422     {
24423         if (!this.IsLongEnough(word, nb))
24424         {
24425             return false;
24426         }
24427
24428         var characterSetChecks = new Array(
24429             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24430             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24431         );
24432         
24433         for (var index = 0; index < word.length; ++index) {
24434             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24435                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24436                     characterSetChecks[nCharSet].fResult = true;
24437                     break;
24438                 }
24439             }
24440         }
24441
24442         var nCharSets = 0;
24443         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24444             if (characterSetChecks[nCharSet].fResult) {
24445                 ++nCharSets;
24446             }
24447         }
24448
24449         if (nCharSets < nb) {
24450             return false;
24451         }
24452         return true;
24453     },
24454     // private
24455     ClientSideStrongPassword: function (pwd)
24456     {
24457         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24458     },
24459     // private
24460     ClientSideMediumPassword: function (pwd)
24461     {
24462         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24463     },
24464     // private
24465     ClientSideWeakPassword: function (pwd)
24466     {
24467         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24468     }
24469           
24470 })//<script type="text/javascript">
24471
24472 /*
24473  * Based  Ext JS Library 1.1.1
24474  * Copyright(c) 2006-2007, Ext JS, LLC.
24475  * LGPL
24476  *
24477  */
24478  
24479 /**
24480  * @class Roo.HtmlEditorCore
24481  * @extends Roo.Component
24482  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24483  *
24484  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24485  */
24486
24487 Roo.HtmlEditorCore = function(config){
24488     
24489     
24490     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24491     
24492     
24493     this.addEvents({
24494         /**
24495          * @event initialize
24496          * Fires when the editor is fully initialized (including the iframe)
24497          * @param {Roo.HtmlEditorCore} this
24498          */
24499         initialize: true,
24500         /**
24501          * @event activate
24502          * Fires when the editor is first receives the focus. Any insertion must wait
24503          * until after this event.
24504          * @param {Roo.HtmlEditorCore} this
24505          */
24506         activate: true,
24507          /**
24508          * @event beforesync
24509          * Fires before the textarea is updated with content from the editor iframe. Return false
24510          * to cancel the sync.
24511          * @param {Roo.HtmlEditorCore} this
24512          * @param {String} html
24513          */
24514         beforesync: true,
24515          /**
24516          * @event beforepush
24517          * Fires before the iframe editor is updated with content from the textarea. Return false
24518          * to cancel the push.
24519          * @param {Roo.HtmlEditorCore} this
24520          * @param {String} html
24521          */
24522         beforepush: true,
24523          /**
24524          * @event sync
24525          * Fires when the textarea is updated with content from the editor iframe.
24526          * @param {Roo.HtmlEditorCore} this
24527          * @param {String} html
24528          */
24529         sync: true,
24530          /**
24531          * @event push
24532          * Fires when the iframe editor is updated with content from the textarea.
24533          * @param {Roo.HtmlEditorCore} this
24534          * @param {String} html
24535          */
24536         push: true,
24537         
24538         /**
24539          * @event editorevent
24540          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24541          * @param {Roo.HtmlEditorCore} this
24542          */
24543         editorevent: true
24544         
24545     });
24546     
24547     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24548     
24549     // defaults : white / black...
24550     this.applyBlacklists();
24551     
24552     
24553     
24554 };
24555
24556
24557 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24558
24559
24560      /**
24561      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24562      */
24563     
24564     owner : false,
24565     
24566      /**
24567      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24568      *                        Roo.resizable.
24569      */
24570     resizable : false,
24571      /**
24572      * @cfg {Number} height (in pixels)
24573      */   
24574     height: 300,
24575    /**
24576      * @cfg {Number} width (in pixels)
24577      */   
24578     width: 500,
24579     
24580     /**
24581      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24582      * 
24583      */
24584     stylesheets: false,
24585     
24586     // id of frame..
24587     frameId: false,
24588     
24589     // private properties
24590     validationEvent : false,
24591     deferHeight: true,
24592     initialized : false,
24593     activated : false,
24594     sourceEditMode : false,
24595     onFocus : Roo.emptyFn,
24596     iframePad:3,
24597     hideMode:'offsets',
24598     
24599     clearUp: true,
24600     
24601     // blacklist + whitelisted elements..
24602     black: false,
24603     white: false,
24604      
24605     bodyCls : '',
24606
24607     /**
24608      * Protected method that will not generally be called directly. It
24609      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24610      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24611      */
24612     getDocMarkup : function(){
24613         // body styles..
24614         var st = '';
24615         
24616         // inherit styels from page...?? 
24617         if (this.stylesheets === false) {
24618             
24619             Roo.get(document.head).select('style').each(function(node) {
24620                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24621             });
24622             
24623             Roo.get(document.head).select('link').each(function(node) { 
24624                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24625             });
24626             
24627         } else if (!this.stylesheets.length) {
24628                 // simple..
24629                 st = '<style type="text/css">' +
24630                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24631                    '</style>';
24632         } else {
24633             for (var i in this.stylesheets) { 
24634                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24635             }
24636             
24637         }
24638         
24639         st +=  '<style type="text/css">' +
24640             'IMG { cursor: pointer } ' +
24641         '</style>';
24642
24643         var cls = 'roo-htmleditor-body';
24644         
24645         if(this.bodyCls.length){
24646             cls += ' ' + this.bodyCls;
24647         }
24648         
24649         return '<html><head>' + st  +
24650             //<style type="text/css">' +
24651             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24652             //'</style>' +
24653             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24654     },
24655
24656     // private
24657     onRender : function(ct, position)
24658     {
24659         var _t = this;
24660         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24661         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24662         
24663         
24664         this.el.dom.style.border = '0 none';
24665         this.el.dom.setAttribute('tabIndex', -1);
24666         this.el.addClass('x-hidden hide');
24667         
24668         
24669         
24670         if(Roo.isIE){ // fix IE 1px bogus margin
24671             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24672         }
24673        
24674         
24675         this.frameId = Roo.id();
24676         
24677          
24678         
24679         var iframe = this.owner.wrap.createChild({
24680             tag: 'iframe',
24681             cls: 'form-control', // bootstrap..
24682             id: this.frameId,
24683             name: this.frameId,
24684             frameBorder : 'no',
24685             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24686         }, this.el
24687         );
24688         
24689         
24690         this.iframe = iframe.dom;
24691
24692          this.assignDocWin();
24693         
24694         this.doc.designMode = 'on';
24695        
24696         this.doc.open();
24697         this.doc.write(this.getDocMarkup());
24698         this.doc.close();
24699
24700         
24701         var task = { // must defer to wait for browser to be ready
24702             run : function(){
24703                 //console.log("run task?" + this.doc.readyState);
24704                 this.assignDocWin();
24705                 if(this.doc.body || this.doc.readyState == 'complete'){
24706                     try {
24707                         this.doc.designMode="on";
24708                     } catch (e) {
24709                         return;
24710                     }
24711                     Roo.TaskMgr.stop(task);
24712                     this.initEditor.defer(10, this);
24713                 }
24714             },
24715             interval : 10,
24716             duration: 10000,
24717             scope: this
24718         };
24719         Roo.TaskMgr.start(task);
24720
24721     },
24722
24723     // private
24724     onResize : function(w, h)
24725     {
24726          Roo.log('resize: ' +w + ',' + h );
24727         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24728         if(!this.iframe){
24729             return;
24730         }
24731         if(typeof w == 'number'){
24732             
24733             this.iframe.style.width = w + 'px';
24734         }
24735         if(typeof h == 'number'){
24736             
24737             this.iframe.style.height = h + 'px';
24738             if(this.doc){
24739                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24740             }
24741         }
24742         
24743     },
24744
24745     /**
24746      * Toggles the editor between standard and source edit mode.
24747      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24748      */
24749     toggleSourceEdit : function(sourceEditMode){
24750         
24751         this.sourceEditMode = sourceEditMode === true;
24752         
24753         if(this.sourceEditMode){
24754  
24755             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24756             
24757         }else{
24758             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24759             //this.iframe.className = '';
24760             this.deferFocus();
24761         }
24762         //this.setSize(this.owner.wrap.getSize());
24763         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24764     },
24765
24766     
24767   
24768
24769     /**
24770      * Protected method that will not generally be called directly. If you need/want
24771      * custom HTML cleanup, this is the method you should override.
24772      * @param {String} html The HTML to be cleaned
24773      * return {String} The cleaned HTML
24774      */
24775     cleanHtml : function(html){
24776         html = String(html);
24777         if(html.length > 5){
24778             if(Roo.isSafari){ // strip safari nonsense
24779                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24780             }
24781         }
24782         if(html == '&nbsp;'){
24783             html = '';
24784         }
24785         return html;
24786     },
24787
24788     /**
24789      * HTML Editor -> Textarea
24790      * Protected method that will not generally be called directly. Syncs the contents
24791      * of the editor iframe with the textarea.
24792      */
24793     syncValue : function(){
24794         if(this.initialized){
24795             var bd = (this.doc.body || this.doc.documentElement);
24796             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24797             var html = bd.innerHTML;
24798             if(Roo.isSafari){
24799                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24800                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24801                 if(m && m[1]){
24802                     html = '<div style="'+m[0]+'">' + html + '</div>';
24803                 }
24804             }
24805             html = this.cleanHtml(html);
24806             // fix up the special chars.. normaly like back quotes in word...
24807             // however we do not want to do this with chinese..
24808             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24809                 
24810                 var cc = match.charCodeAt();
24811
24812                 // Get the character value, handling surrogate pairs
24813                 if (match.length == 2) {
24814                     // It's a surrogate pair, calculate the Unicode code point
24815                     var high = match.charCodeAt(0) - 0xD800;
24816                     var low  = match.charCodeAt(1) - 0xDC00;
24817                     cc = (high * 0x400) + low + 0x10000;
24818                 }  else if (
24819                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24820                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24821                     (cc >= 0xf900 && cc < 0xfb00 )
24822                 ) {
24823                         return match;
24824                 }  
24825          
24826                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24827                 return "&#" + cc + ";";
24828                 
24829                 
24830             });
24831             
24832             
24833              
24834             if(this.owner.fireEvent('beforesync', this, html) !== false){
24835                 this.el.dom.value = html;
24836                 this.owner.fireEvent('sync', this, html);
24837             }
24838         }
24839     },
24840
24841     /**
24842      * Protected method that will not generally be called directly. Pushes the value of the textarea
24843      * into the iframe editor.
24844      */
24845     pushValue : function(){
24846         if(this.initialized){
24847             var v = this.el.dom.value.trim();
24848             
24849 //            if(v.length < 1){
24850 //                v = '&#160;';
24851 //            }
24852             
24853             if(this.owner.fireEvent('beforepush', this, v) !== false){
24854                 var d = (this.doc.body || this.doc.documentElement);
24855                 d.innerHTML = v;
24856                 this.cleanUpPaste();
24857                 this.el.dom.value = d.innerHTML;
24858                 this.owner.fireEvent('push', this, v);
24859             }
24860         }
24861     },
24862
24863     // private
24864     deferFocus : function(){
24865         this.focus.defer(10, this);
24866     },
24867
24868     // doc'ed in Field
24869     focus : function(){
24870         if(this.win && !this.sourceEditMode){
24871             this.win.focus();
24872         }else{
24873             this.el.focus();
24874         }
24875     },
24876     
24877     assignDocWin: function()
24878     {
24879         var iframe = this.iframe;
24880         
24881          if(Roo.isIE){
24882             this.doc = iframe.contentWindow.document;
24883             this.win = iframe.contentWindow;
24884         } else {
24885 //            if (!Roo.get(this.frameId)) {
24886 //                return;
24887 //            }
24888 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24889 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24890             
24891             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24892                 return;
24893             }
24894             
24895             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24896             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24897         }
24898     },
24899     
24900     // private
24901     initEditor : function(){
24902         //console.log("INIT EDITOR");
24903         this.assignDocWin();
24904         
24905         
24906         
24907         this.doc.designMode="on";
24908         this.doc.open();
24909         this.doc.write(this.getDocMarkup());
24910         this.doc.close();
24911         
24912         var dbody = (this.doc.body || this.doc.documentElement);
24913         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24914         // this copies styles from the containing element into thsi one..
24915         // not sure why we need all of this..
24916         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24917         
24918         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24919         //ss['background-attachment'] = 'fixed'; // w3c
24920         dbody.bgProperties = 'fixed'; // ie
24921         //Roo.DomHelper.applyStyles(dbody, ss);
24922         Roo.EventManager.on(this.doc, {
24923             //'mousedown': this.onEditorEvent,
24924             'mouseup': this.onEditorEvent,
24925             'dblclick': this.onEditorEvent,
24926             'click': this.onEditorEvent,
24927             'keyup': this.onEditorEvent,
24928             buffer:100,
24929             scope: this
24930         });
24931         if(Roo.isGecko){
24932             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24933         }
24934         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24935             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24936         }
24937         this.initialized = true;
24938
24939         this.owner.fireEvent('initialize', this);
24940         this.pushValue();
24941     },
24942
24943     // private
24944     onDestroy : function(){
24945         
24946         
24947         
24948         if(this.rendered){
24949             
24950             //for (var i =0; i < this.toolbars.length;i++) {
24951             //    // fixme - ask toolbars for heights?
24952             //    this.toolbars[i].onDestroy();
24953            // }
24954             
24955             //this.wrap.dom.innerHTML = '';
24956             //this.wrap.remove();
24957         }
24958     },
24959
24960     // private
24961     onFirstFocus : function(){
24962         
24963         this.assignDocWin();
24964         
24965         
24966         this.activated = true;
24967          
24968     
24969         if(Roo.isGecko){ // prevent silly gecko errors
24970             this.win.focus();
24971             var s = this.win.getSelection();
24972             if(!s.focusNode || s.focusNode.nodeType != 3){
24973                 var r = s.getRangeAt(0);
24974                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24975                 r.collapse(true);
24976                 this.deferFocus();
24977             }
24978             try{
24979                 this.execCmd('useCSS', true);
24980                 this.execCmd('styleWithCSS', false);
24981             }catch(e){}
24982         }
24983         this.owner.fireEvent('activate', this);
24984     },
24985
24986     // private
24987     adjustFont: function(btn){
24988         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24989         //if(Roo.isSafari){ // safari
24990         //    adjust *= 2;
24991        // }
24992         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24993         if(Roo.isSafari){ // safari
24994             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24995             v =  (v < 10) ? 10 : v;
24996             v =  (v > 48) ? 48 : v;
24997             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24998             
24999         }
25000         
25001         
25002         v = Math.max(1, v+adjust);
25003         
25004         this.execCmd('FontSize', v  );
25005     },
25006
25007     onEditorEvent : function(e)
25008     {
25009         this.owner.fireEvent('editorevent', this, e);
25010       //  this.updateToolbar();
25011         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25012     },
25013
25014     insertTag : function(tg)
25015     {
25016         // could be a bit smarter... -> wrap the current selected tRoo..
25017         if (tg.toLowerCase() == 'span' ||
25018             tg.toLowerCase() == 'code' ||
25019             tg.toLowerCase() == 'sup' ||
25020             tg.toLowerCase() == 'sub' 
25021             ) {
25022             
25023             range = this.createRange(this.getSelection());
25024             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25025             wrappingNode.appendChild(range.extractContents());
25026             range.insertNode(wrappingNode);
25027
25028             return;
25029             
25030             
25031             
25032         }
25033         this.execCmd("formatblock",   tg);
25034         
25035     },
25036     
25037     insertText : function(txt)
25038     {
25039         
25040         
25041         var range = this.createRange();
25042         range.deleteContents();
25043                //alert(Sender.getAttribute('label'));
25044                
25045         range.insertNode(this.doc.createTextNode(txt));
25046     } ,
25047     
25048      
25049
25050     /**
25051      * Executes a Midas editor command on the editor document and performs necessary focus and
25052      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25053      * @param {String} cmd The Midas command
25054      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25055      */
25056     relayCmd : function(cmd, value){
25057         this.win.focus();
25058         this.execCmd(cmd, value);
25059         this.owner.fireEvent('editorevent', this);
25060         //this.updateToolbar();
25061         this.owner.deferFocus();
25062     },
25063
25064     /**
25065      * Executes a Midas editor command directly on the editor document.
25066      * For visual commands, you should use {@link #relayCmd} instead.
25067      * <b>This should only be called after the editor is initialized.</b>
25068      * @param {String} cmd The Midas command
25069      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25070      */
25071     execCmd : function(cmd, value){
25072         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25073         this.syncValue();
25074     },
25075  
25076  
25077    
25078     /**
25079      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25080      * to insert tRoo.
25081      * @param {String} text | dom node.. 
25082      */
25083     insertAtCursor : function(text)
25084     {
25085         
25086         if(!this.activated){
25087             return;
25088         }
25089         /*
25090         if(Roo.isIE){
25091             this.win.focus();
25092             var r = this.doc.selection.createRange();
25093             if(r){
25094                 r.collapse(true);
25095                 r.pasteHTML(text);
25096                 this.syncValue();
25097                 this.deferFocus();
25098             
25099             }
25100             return;
25101         }
25102         */
25103         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25104             this.win.focus();
25105             
25106             
25107             // from jquery ui (MIT licenced)
25108             var range, node;
25109             var win = this.win;
25110             
25111             if (win.getSelection && win.getSelection().getRangeAt) {
25112                 range = win.getSelection().getRangeAt(0);
25113                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25114                 range.insertNode(node);
25115             } else if (win.document.selection && win.document.selection.createRange) {
25116                 // no firefox support
25117                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25118                 win.document.selection.createRange().pasteHTML(txt);
25119             } else {
25120                 // no firefox support
25121                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25122                 this.execCmd('InsertHTML', txt);
25123             } 
25124             
25125             this.syncValue();
25126             
25127             this.deferFocus();
25128         }
25129     },
25130  // private
25131     mozKeyPress : function(e){
25132         if(e.ctrlKey){
25133             var c = e.getCharCode(), cmd;
25134           
25135             if(c > 0){
25136                 c = String.fromCharCode(c).toLowerCase();
25137                 switch(c){
25138                     case 'b':
25139                         cmd = 'bold';
25140                         break;
25141                     case 'i':
25142                         cmd = 'italic';
25143                         break;
25144                     
25145                     case 'u':
25146                         cmd = 'underline';
25147                         break;
25148                     
25149                     case 'v':
25150                         this.cleanUpPaste.defer(100, this);
25151                         return;
25152                         
25153                 }
25154                 if(cmd){
25155                     this.win.focus();
25156                     this.execCmd(cmd);
25157                     this.deferFocus();
25158                     e.preventDefault();
25159                 }
25160                 
25161             }
25162         }
25163     },
25164
25165     // private
25166     fixKeys : function(){ // load time branching for fastest keydown performance
25167         if(Roo.isIE){
25168             return function(e){
25169                 var k = e.getKey(), r;
25170                 if(k == e.TAB){
25171                     e.stopEvent();
25172                     r = this.doc.selection.createRange();
25173                     if(r){
25174                         r.collapse(true);
25175                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25176                         this.deferFocus();
25177                     }
25178                     return;
25179                 }
25180                 
25181                 if(k == e.ENTER){
25182                     r = this.doc.selection.createRange();
25183                     if(r){
25184                         var target = r.parentElement();
25185                         if(!target || target.tagName.toLowerCase() != 'li'){
25186                             e.stopEvent();
25187                             r.pasteHTML('<br />');
25188                             r.collapse(false);
25189                             r.select();
25190                         }
25191                     }
25192                 }
25193                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25194                     this.cleanUpPaste.defer(100, this);
25195                     return;
25196                 }
25197                 
25198                 
25199             };
25200         }else if(Roo.isOpera){
25201             return function(e){
25202                 var k = e.getKey();
25203                 if(k == e.TAB){
25204                     e.stopEvent();
25205                     this.win.focus();
25206                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25207                     this.deferFocus();
25208                 }
25209                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25210                     this.cleanUpPaste.defer(100, this);
25211                     return;
25212                 }
25213                 
25214             };
25215         }else if(Roo.isSafari){
25216             return function(e){
25217                 var k = e.getKey();
25218                 
25219                 if(k == e.TAB){
25220                     e.stopEvent();
25221                     this.execCmd('InsertText','\t');
25222                     this.deferFocus();
25223                     return;
25224                 }
25225                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25226                     this.cleanUpPaste.defer(100, this);
25227                     return;
25228                 }
25229                 
25230              };
25231         }
25232     }(),
25233     
25234     getAllAncestors: function()
25235     {
25236         var p = this.getSelectedNode();
25237         var a = [];
25238         if (!p) {
25239             a.push(p); // push blank onto stack..
25240             p = this.getParentElement();
25241         }
25242         
25243         
25244         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25245             a.push(p);
25246             p = p.parentNode;
25247         }
25248         a.push(this.doc.body);
25249         return a;
25250     },
25251     lastSel : false,
25252     lastSelNode : false,
25253     
25254     
25255     getSelection : function() 
25256     {
25257         this.assignDocWin();
25258         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25259     },
25260     
25261     getSelectedNode: function() 
25262     {
25263         // this may only work on Gecko!!!
25264         
25265         // should we cache this!!!!
25266         
25267         
25268         
25269          
25270         var range = this.createRange(this.getSelection()).cloneRange();
25271         
25272         if (Roo.isIE) {
25273             var parent = range.parentElement();
25274             while (true) {
25275                 var testRange = range.duplicate();
25276                 testRange.moveToElementText(parent);
25277                 if (testRange.inRange(range)) {
25278                     break;
25279                 }
25280                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25281                     break;
25282                 }
25283                 parent = parent.parentElement;
25284             }
25285             return parent;
25286         }
25287         
25288         // is ancestor a text element.
25289         var ac =  range.commonAncestorContainer;
25290         if (ac.nodeType == 3) {
25291             ac = ac.parentNode;
25292         }
25293         
25294         var ar = ac.childNodes;
25295          
25296         var nodes = [];
25297         var other_nodes = [];
25298         var has_other_nodes = false;
25299         for (var i=0;i<ar.length;i++) {
25300             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25301                 continue;
25302             }
25303             // fullly contained node.
25304             
25305             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25306                 nodes.push(ar[i]);
25307                 continue;
25308             }
25309             
25310             // probably selected..
25311             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25312                 other_nodes.push(ar[i]);
25313                 continue;
25314             }
25315             // outer..
25316             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25317                 continue;
25318             }
25319             
25320             
25321             has_other_nodes = true;
25322         }
25323         if (!nodes.length && other_nodes.length) {
25324             nodes= other_nodes;
25325         }
25326         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25327             return false;
25328         }
25329         
25330         return nodes[0];
25331     },
25332     createRange: function(sel)
25333     {
25334         // this has strange effects when using with 
25335         // top toolbar - not sure if it's a great idea.
25336         //this.editor.contentWindow.focus();
25337         if (typeof sel != "undefined") {
25338             try {
25339                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25340             } catch(e) {
25341                 return this.doc.createRange();
25342             }
25343         } else {
25344             return this.doc.createRange();
25345         }
25346     },
25347     getParentElement: function()
25348     {
25349         
25350         this.assignDocWin();
25351         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25352         
25353         var range = this.createRange(sel);
25354          
25355         try {
25356             var p = range.commonAncestorContainer;
25357             while (p.nodeType == 3) { // text node
25358                 p = p.parentNode;
25359             }
25360             return p;
25361         } catch (e) {
25362             return null;
25363         }
25364     
25365     },
25366     /***
25367      *
25368      * Range intersection.. the hard stuff...
25369      *  '-1' = before
25370      *  '0' = hits..
25371      *  '1' = after.
25372      *         [ -- selected range --- ]
25373      *   [fail]                        [fail]
25374      *
25375      *    basically..
25376      *      if end is before start or  hits it. fail.
25377      *      if start is after end or hits it fail.
25378      *
25379      *   if either hits (but other is outside. - then it's not 
25380      *   
25381      *    
25382      **/
25383     
25384     
25385     // @see http://www.thismuchiknow.co.uk/?p=64.
25386     rangeIntersectsNode : function(range, node)
25387     {
25388         var nodeRange = node.ownerDocument.createRange();
25389         try {
25390             nodeRange.selectNode(node);
25391         } catch (e) {
25392             nodeRange.selectNodeContents(node);
25393         }
25394     
25395         var rangeStartRange = range.cloneRange();
25396         rangeStartRange.collapse(true);
25397     
25398         var rangeEndRange = range.cloneRange();
25399         rangeEndRange.collapse(false);
25400     
25401         var nodeStartRange = nodeRange.cloneRange();
25402         nodeStartRange.collapse(true);
25403     
25404         var nodeEndRange = nodeRange.cloneRange();
25405         nodeEndRange.collapse(false);
25406     
25407         return rangeStartRange.compareBoundaryPoints(
25408                  Range.START_TO_START, nodeEndRange) == -1 &&
25409                rangeEndRange.compareBoundaryPoints(
25410                  Range.START_TO_START, nodeStartRange) == 1;
25411         
25412          
25413     },
25414     rangeCompareNode : function(range, node)
25415     {
25416         var nodeRange = node.ownerDocument.createRange();
25417         try {
25418             nodeRange.selectNode(node);
25419         } catch (e) {
25420             nodeRange.selectNodeContents(node);
25421         }
25422         
25423         
25424         range.collapse(true);
25425     
25426         nodeRange.collapse(true);
25427      
25428         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25429         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25430          
25431         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25432         
25433         var nodeIsBefore   =  ss == 1;
25434         var nodeIsAfter    = ee == -1;
25435         
25436         if (nodeIsBefore && nodeIsAfter) {
25437             return 0; // outer
25438         }
25439         if (!nodeIsBefore && nodeIsAfter) {
25440             return 1; //right trailed.
25441         }
25442         
25443         if (nodeIsBefore && !nodeIsAfter) {
25444             return 2;  // left trailed.
25445         }
25446         // fully contined.
25447         return 3;
25448     },
25449
25450     // private? - in a new class?
25451     cleanUpPaste :  function()
25452     {
25453         // cleans up the whole document..
25454         Roo.log('cleanuppaste');
25455         
25456         this.cleanUpChildren(this.doc.body);
25457         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25458         if (clean != this.doc.body.innerHTML) {
25459             this.doc.body.innerHTML = clean;
25460         }
25461         
25462     },
25463     
25464     cleanWordChars : function(input) {// change the chars to hex code
25465         var he = Roo.HtmlEditorCore;
25466         
25467         var output = input;
25468         Roo.each(he.swapCodes, function(sw) { 
25469             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25470             
25471             output = output.replace(swapper, sw[1]);
25472         });
25473         
25474         return output;
25475     },
25476     
25477     
25478     cleanUpChildren : function (n)
25479     {
25480         if (!n.childNodes.length) {
25481             return;
25482         }
25483         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25484            this.cleanUpChild(n.childNodes[i]);
25485         }
25486     },
25487     
25488     
25489         
25490     
25491     cleanUpChild : function (node)
25492     {
25493         var ed = this;
25494         //console.log(node);
25495         if (node.nodeName == "#text") {
25496             // clean up silly Windows -- stuff?
25497             return; 
25498         }
25499         if (node.nodeName == "#comment") {
25500             node.parentNode.removeChild(node);
25501             // clean up silly Windows -- stuff?
25502             return; 
25503         }
25504         var lcname = node.tagName.toLowerCase();
25505         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25506         // whitelist of tags..
25507         
25508         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25509             // remove node.
25510             node.parentNode.removeChild(node);
25511             return;
25512             
25513         }
25514         
25515         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25516         
25517         // spans with no attributes - just remove them..
25518         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25519             remove_keep_children = true;
25520         }
25521         
25522         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25523         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25524         
25525         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25526         //    remove_keep_children = true;
25527         //}
25528         
25529         if (remove_keep_children) {
25530             this.cleanUpChildren(node);
25531             // inserts everything just before this node...
25532             while (node.childNodes.length) {
25533                 var cn = node.childNodes[0];
25534                 node.removeChild(cn);
25535                 node.parentNode.insertBefore(cn, node);
25536             }
25537             node.parentNode.removeChild(node);
25538             return;
25539         }
25540         
25541         if (!node.attributes || !node.attributes.length) {
25542             
25543           
25544             
25545             
25546             this.cleanUpChildren(node);
25547             return;
25548         }
25549         
25550         function cleanAttr(n,v)
25551         {
25552             
25553             if (v.match(/^\./) || v.match(/^\//)) {
25554                 return;
25555             }
25556             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25557                 return;
25558             }
25559             if (v.match(/^#/)) {
25560                 return;
25561             }
25562             if (v.match(/^\{/)) { // allow template editing.
25563                 return;
25564             }
25565 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25566             node.removeAttribute(n);
25567             
25568         }
25569         
25570         var cwhite = this.cwhite;
25571         var cblack = this.cblack;
25572             
25573         function cleanStyle(n,v)
25574         {
25575             if (v.match(/expression/)) { //XSS?? should we even bother..
25576                 node.removeAttribute(n);
25577                 return;
25578             }
25579             
25580             var parts = v.split(/;/);
25581             var clean = [];
25582             
25583             Roo.each(parts, function(p) {
25584                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25585                 if (!p.length) {
25586                     return true;
25587                 }
25588                 var l = p.split(':').shift().replace(/\s+/g,'');
25589                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25590                 
25591                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25592 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25593                     //node.removeAttribute(n);
25594                     return true;
25595                 }
25596                 //Roo.log()
25597                 // only allow 'c whitelisted system attributes'
25598                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25599 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25600                     //node.removeAttribute(n);
25601                     return true;
25602                 }
25603                 
25604                 
25605                  
25606                 
25607                 clean.push(p);
25608                 return true;
25609             });
25610             if (clean.length) { 
25611                 node.setAttribute(n, clean.join(';'));
25612             } else {
25613                 node.removeAttribute(n);
25614             }
25615             
25616         }
25617         
25618         
25619         for (var i = node.attributes.length-1; i > -1 ; i--) {
25620             var a = node.attributes[i];
25621             //console.log(a);
25622             
25623             if (a.name.toLowerCase().substr(0,2)=='on')  {
25624                 node.removeAttribute(a.name);
25625                 continue;
25626             }
25627             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25628                 node.removeAttribute(a.name);
25629                 continue;
25630             }
25631             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25632                 cleanAttr(a.name,a.value); // fixme..
25633                 continue;
25634             }
25635             if (a.name == 'style') {
25636                 cleanStyle(a.name,a.value);
25637                 continue;
25638             }
25639             /// clean up MS crap..
25640             // tecnically this should be a list of valid class'es..
25641             
25642             
25643             if (a.name == 'class') {
25644                 if (a.value.match(/^Mso/)) {
25645                     node.removeAttribute('class');
25646                 }
25647                 
25648                 if (a.value.match(/^body$/)) {
25649                     node.removeAttribute('class');
25650                 }
25651                 continue;
25652             }
25653             
25654             // style cleanup!?
25655             // class cleanup?
25656             
25657         }
25658         
25659         
25660         this.cleanUpChildren(node);
25661         
25662         
25663     },
25664     
25665     /**
25666      * Clean up MS wordisms...
25667      */
25668     cleanWord : function(node)
25669     {
25670         if (!node) {
25671             this.cleanWord(this.doc.body);
25672             return;
25673         }
25674         
25675         if(
25676                 node.nodeName == 'SPAN' &&
25677                 !node.hasAttributes() &&
25678                 node.childNodes.length == 1 &&
25679                 node.firstChild.nodeName == "#text"  
25680         ) {
25681             var textNode = node.firstChild;
25682             node.removeChild(textNode);
25683             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25684                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25685             }
25686             node.parentNode.insertBefore(textNode, node);
25687             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25688                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25689             }
25690             node.parentNode.removeChild(node);
25691         }
25692         
25693         if (node.nodeName == "#text") {
25694             // clean up silly Windows -- stuff?
25695             return; 
25696         }
25697         if (node.nodeName == "#comment") {
25698             node.parentNode.removeChild(node);
25699             // clean up silly Windows -- stuff?
25700             return; 
25701         }
25702         
25703         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25704             node.parentNode.removeChild(node);
25705             return;
25706         }
25707         //Roo.log(node.tagName);
25708         // remove - but keep children..
25709         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25710             //Roo.log('-- removed');
25711             while (node.childNodes.length) {
25712                 var cn = node.childNodes[0];
25713                 node.removeChild(cn);
25714                 node.parentNode.insertBefore(cn, node);
25715                 // move node to parent - and clean it..
25716                 this.cleanWord(cn);
25717             }
25718             node.parentNode.removeChild(node);
25719             /// no need to iterate chidlren = it's got none..
25720             //this.iterateChildren(node, this.cleanWord);
25721             return;
25722         }
25723         // clean styles
25724         if (node.className.length) {
25725             
25726             var cn = node.className.split(/\W+/);
25727             var cna = [];
25728             Roo.each(cn, function(cls) {
25729                 if (cls.match(/Mso[a-zA-Z]+/)) {
25730                     return;
25731                 }
25732                 cna.push(cls);
25733             });
25734             node.className = cna.length ? cna.join(' ') : '';
25735             if (!cna.length) {
25736                 node.removeAttribute("class");
25737             }
25738         }
25739         
25740         if (node.hasAttribute("lang")) {
25741             node.removeAttribute("lang");
25742         }
25743         
25744         if (node.hasAttribute("style")) {
25745             
25746             var styles = node.getAttribute("style").split(";");
25747             var nstyle = [];
25748             Roo.each(styles, function(s) {
25749                 if (!s.match(/:/)) {
25750                     return;
25751                 }
25752                 var kv = s.split(":");
25753                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25754                     return;
25755                 }
25756                 // what ever is left... we allow.
25757                 nstyle.push(s);
25758             });
25759             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25760             if (!nstyle.length) {
25761                 node.removeAttribute('style');
25762             }
25763         }
25764         this.iterateChildren(node, this.cleanWord);
25765         
25766         
25767         
25768     },
25769     /**
25770      * iterateChildren of a Node, calling fn each time, using this as the scole..
25771      * @param {DomNode} node node to iterate children of.
25772      * @param {Function} fn method of this class to call on each item.
25773      */
25774     iterateChildren : function(node, fn)
25775     {
25776         if (!node.childNodes.length) {
25777                 return;
25778         }
25779         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25780            fn.call(this, node.childNodes[i])
25781         }
25782     },
25783     
25784     
25785     /**
25786      * cleanTableWidths.
25787      *
25788      * Quite often pasting from word etc.. results in tables with column and widths.
25789      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25790      *
25791      */
25792     cleanTableWidths : function(node)
25793     {
25794          
25795          
25796         if (!node) {
25797             this.cleanTableWidths(this.doc.body);
25798             return;
25799         }
25800         
25801         // ignore list...
25802         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25803             return; 
25804         }
25805         Roo.log(node.tagName);
25806         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25807             this.iterateChildren(node, this.cleanTableWidths);
25808             return;
25809         }
25810         if (node.hasAttribute('width')) {
25811             node.removeAttribute('width');
25812         }
25813         
25814          
25815         if (node.hasAttribute("style")) {
25816             // pretty basic...
25817             
25818             var styles = node.getAttribute("style").split(";");
25819             var nstyle = [];
25820             Roo.each(styles, function(s) {
25821                 if (!s.match(/:/)) {
25822                     return;
25823                 }
25824                 var kv = s.split(":");
25825                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25826                     return;
25827                 }
25828                 // what ever is left... we allow.
25829                 nstyle.push(s);
25830             });
25831             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25832             if (!nstyle.length) {
25833                 node.removeAttribute('style');
25834             }
25835         }
25836         
25837         this.iterateChildren(node, this.cleanTableWidths);
25838         
25839         
25840     },
25841     
25842     
25843     
25844     
25845     domToHTML : function(currentElement, depth, nopadtext) {
25846         
25847         depth = depth || 0;
25848         nopadtext = nopadtext || false;
25849     
25850         if (!currentElement) {
25851             return this.domToHTML(this.doc.body);
25852         }
25853         
25854         //Roo.log(currentElement);
25855         var j;
25856         var allText = false;
25857         var nodeName = currentElement.nodeName;
25858         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25859         
25860         if  (nodeName == '#text') {
25861             
25862             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25863         }
25864         
25865         
25866         var ret = '';
25867         if (nodeName != 'BODY') {
25868              
25869             var i = 0;
25870             // Prints the node tagName, such as <A>, <IMG>, etc
25871             if (tagName) {
25872                 var attr = [];
25873                 for(i = 0; i < currentElement.attributes.length;i++) {
25874                     // quoting?
25875                     var aname = currentElement.attributes.item(i).name;
25876                     if (!currentElement.attributes.item(i).value.length) {
25877                         continue;
25878                     }
25879                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25880                 }
25881                 
25882                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25883             } 
25884             else {
25885                 
25886                 // eack
25887             }
25888         } else {
25889             tagName = false;
25890         }
25891         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25892             return ret;
25893         }
25894         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25895             nopadtext = true;
25896         }
25897         
25898         
25899         // Traverse the tree
25900         i = 0;
25901         var currentElementChild = currentElement.childNodes.item(i);
25902         var allText = true;
25903         var innerHTML  = '';
25904         lastnode = '';
25905         while (currentElementChild) {
25906             // Formatting code (indent the tree so it looks nice on the screen)
25907             var nopad = nopadtext;
25908             if (lastnode == 'SPAN') {
25909                 nopad  = true;
25910             }
25911             // text
25912             if  (currentElementChild.nodeName == '#text') {
25913                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25914                 toadd = nopadtext ? toadd : toadd.trim();
25915                 if (!nopad && toadd.length > 80) {
25916                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25917                 }
25918                 innerHTML  += toadd;
25919                 
25920                 i++;
25921                 currentElementChild = currentElement.childNodes.item(i);
25922                 lastNode = '';
25923                 continue;
25924             }
25925             allText = false;
25926             
25927             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25928                 
25929             // Recursively traverse the tree structure of the child node
25930             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25931             lastnode = currentElementChild.nodeName;
25932             i++;
25933             currentElementChild=currentElement.childNodes.item(i);
25934         }
25935         
25936         ret += innerHTML;
25937         
25938         if (!allText) {
25939                 // The remaining code is mostly for formatting the tree
25940             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25941         }
25942         
25943         
25944         if (tagName) {
25945             ret+= "</"+tagName+">";
25946         }
25947         return ret;
25948         
25949     },
25950         
25951     applyBlacklists : function()
25952     {
25953         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25954         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25955         
25956         this.white = [];
25957         this.black = [];
25958         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25959             if (b.indexOf(tag) > -1) {
25960                 return;
25961             }
25962             this.white.push(tag);
25963             
25964         }, this);
25965         
25966         Roo.each(w, function(tag) {
25967             if (b.indexOf(tag) > -1) {
25968                 return;
25969             }
25970             if (this.white.indexOf(tag) > -1) {
25971                 return;
25972             }
25973             this.white.push(tag);
25974             
25975         }, this);
25976         
25977         
25978         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25979             if (w.indexOf(tag) > -1) {
25980                 return;
25981             }
25982             this.black.push(tag);
25983             
25984         }, this);
25985         
25986         Roo.each(b, function(tag) {
25987             if (w.indexOf(tag) > -1) {
25988                 return;
25989             }
25990             if (this.black.indexOf(tag) > -1) {
25991                 return;
25992             }
25993             this.black.push(tag);
25994             
25995         }, this);
25996         
25997         
25998         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25999         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26000         
26001         this.cwhite = [];
26002         this.cblack = [];
26003         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26004             if (b.indexOf(tag) > -1) {
26005                 return;
26006             }
26007             this.cwhite.push(tag);
26008             
26009         }, this);
26010         
26011         Roo.each(w, function(tag) {
26012             if (b.indexOf(tag) > -1) {
26013                 return;
26014             }
26015             if (this.cwhite.indexOf(tag) > -1) {
26016                 return;
26017             }
26018             this.cwhite.push(tag);
26019             
26020         }, this);
26021         
26022         
26023         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26024             if (w.indexOf(tag) > -1) {
26025                 return;
26026             }
26027             this.cblack.push(tag);
26028             
26029         }, this);
26030         
26031         Roo.each(b, function(tag) {
26032             if (w.indexOf(tag) > -1) {
26033                 return;
26034             }
26035             if (this.cblack.indexOf(tag) > -1) {
26036                 return;
26037             }
26038             this.cblack.push(tag);
26039             
26040         }, this);
26041     },
26042     
26043     setStylesheets : function(stylesheets)
26044     {
26045         if(typeof(stylesheets) == 'string'){
26046             Roo.get(this.iframe.contentDocument.head).createChild({
26047                 tag : 'link',
26048                 rel : 'stylesheet',
26049                 type : 'text/css',
26050                 href : stylesheets
26051             });
26052             
26053             return;
26054         }
26055         var _this = this;
26056      
26057         Roo.each(stylesheets, function(s) {
26058             if(!s.length){
26059                 return;
26060             }
26061             
26062             Roo.get(_this.iframe.contentDocument.head).createChild({
26063                 tag : 'link',
26064                 rel : 'stylesheet',
26065                 type : 'text/css',
26066                 href : s
26067             });
26068         });
26069
26070         
26071     },
26072     
26073     removeStylesheets : function()
26074     {
26075         var _this = this;
26076         
26077         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26078             s.remove();
26079         });
26080     },
26081     
26082     setStyle : function(style)
26083     {
26084         Roo.get(this.iframe.contentDocument.head).createChild({
26085             tag : 'style',
26086             type : 'text/css',
26087             html : style
26088         });
26089
26090         return;
26091     }
26092     
26093     // hide stuff that is not compatible
26094     /**
26095      * @event blur
26096      * @hide
26097      */
26098     /**
26099      * @event change
26100      * @hide
26101      */
26102     /**
26103      * @event focus
26104      * @hide
26105      */
26106     /**
26107      * @event specialkey
26108      * @hide
26109      */
26110     /**
26111      * @cfg {String} fieldClass @hide
26112      */
26113     /**
26114      * @cfg {String} focusClass @hide
26115      */
26116     /**
26117      * @cfg {String} autoCreate @hide
26118      */
26119     /**
26120      * @cfg {String} inputType @hide
26121      */
26122     /**
26123      * @cfg {String} invalidClass @hide
26124      */
26125     /**
26126      * @cfg {String} invalidText @hide
26127      */
26128     /**
26129      * @cfg {String} msgFx @hide
26130      */
26131     /**
26132      * @cfg {String} validateOnBlur @hide
26133      */
26134 });
26135
26136 Roo.HtmlEditorCore.white = [
26137         'area', 'br', 'img', 'input', 'hr', 'wbr',
26138         
26139        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26140        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26141        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26142        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26143        'table',   'ul',         'xmp', 
26144        
26145        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26146       'thead',   'tr', 
26147      
26148       'dir', 'menu', 'ol', 'ul', 'dl',
26149        
26150       'embed',  'object'
26151 ];
26152
26153
26154 Roo.HtmlEditorCore.black = [
26155     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26156         'applet', // 
26157         'base',   'basefont', 'bgsound', 'blink',  'body', 
26158         'frame',  'frameset', 'head',    'html',   'ilayer', 
26159         'iframe', 'layer',  'link',     'meta',    'object',   
26160         'script', 'style' ,'title',  'xml' // clean later..
26161 ];
26162 Roo.HtmlEditorCore.clean = [
26163     'script', 'style', 'title', 'xml'
26164 ];
26165 Roo.HtmlEditorCore.remove = [
26166     'font'
26167 ];
26168 // attributes..
26169
26170 Roo.HtmlEditorCore.ablack = [
26171     'on'
26172 ];
26173     
26174 Roo.HtmlEditorCore.aclean = [ 
26175     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26176 ];
26177
26178 // protocols..
26179 Roo.HtmlEditorCore.pwhite= [
26180         'http',  'https',  'mailto'
26181 ];
26182
26183 // white listed style attributes.
26184 Roo.HtmlEditorCore.cwhite= [
26185       //  'text-align', /// default is to allow most things..
26186       
26187          
26188 //        'font-size'//??
26189 ];
26190
26191 // black listed style attributes.
26192 Roo.HtmlEditorCore.cblack= [
26193       //  'font-size' -- this can be set by the project 
26194 ];
26195
26196
26197 Roo.HtmlEditorCore.swapCodes   =[ 
26198     [    8211, "&#8211;" ], 
26199     [    8212, "&#8212;" ], 
26200     [    8216,  "'" ],  
26201     [    8217, "'" ],  
26202     [    8220, '"' ],  
26203     [    8221, '"' ],  
26204     [    8226, "*" ],  
26205     [    8230, "..." ]
26206 ]; 
26207
26208     /*
26209  * - LGPL
26210  *
26211  * HtmlEditor
26212  * 
26213  */
26214
26215 /**
26216  * @class Roo.bootstrap.HtmlEditor
26217  * @extends Roo.bootstrap.TextArea
26218  * Bootstrap HtmlEditor class
26219
26220  * @constructor
26221  * Create a new HtmlEditor
26222  * @param {Object} config The config object
26223  */
26224
26225 Roo.bootstrap.HtmlEditor = function(config){
26226     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26227     if (!this.toolbars) {
26228         this.toolbars = [];
26229     }
26230     
26231     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26232     this.addEvents({
26233             /**
26234              * @event initialize
26235              * Fires when the editor is fully initialized (including the iframe)
26236              * @param {HtmlEditor} this
26237              */
26238             initialize: true,
26239             /**
26240              * @event activate
26241              * Fires when the editor is first receives the focus. Any insertion must wait
26242              * until after this event.
26243              * @param {HtmlEditor} this
26244              */
26245             activate: true,
26246              /**
26247              * @event beforesync
26248              * Fires before the textarea is updated with content from the editor iframe. Return false
26249              * to cancel the sync.
26250              * @param {HtmlEditor} this
26251              * @param {String} html
26252              */
26253             beforesync: true,
26254              /**
26255              * @event beforepush
26256              * Fires before the iframe editor is updated with content from the textarea. Return false
26257              * to cancel the push.
26258              * @param {HtmlEditor} this
26259              * @param {String} html
26260              */
26261             beforepush: true,
26262              /**
26263              * @event sync
26264              * Fires when the textarea is updated with content from the editor iframe.
26265              * @param {HtmlEditor} this
26266              * @param {String} html
26267              */
26268             sync: true,
26269              /**
26270              * @event push
26271              * Fires when the iframe editor is updated with content from the textarea.
26272              * @param {HtmlEditor} this
26273              * @param {String} html
26274              */
26275             push: true,
26276              /**
26277              * @event editmodechange
26278              * Fires when the editor switches edit modes
26279              * @param {HtmlEditor} this
26280              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26281              */
26282             editmodechange: true,
26283             /**
26284              * @event editorevent
26285              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26286              * @param {HtmlEditor} this
26287              */
26288             editorevent: true,
26289             /**
26290              * @event firstfocus
26291              * Fires when on first focus - needed by toolbars..
26292              * @param {HtmlEditor} this
26293              */
26294             firstfocus: true,
26295             /**
26296              * @event autosave
26297              * Auto save the htmlEditor value as a file into Events
26298              * @param {HtmlEditor} this
26299              */
26300             autosave: true,
26301             /**
26302              * @event savedpreview
26303              * preview the saved version of htmlEditor
26304              * @param {HtmlEditor} this
26305              */
26306             savedpreview: true
26307         });
26308 };
26309
26310
26311 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26312     
26313     
26314       /**
26315      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26316      */
26317     toolbars : false,
26318     
26319      /**
26320     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26321     */
26322     btns : [],
26323    
26324      /**
26325      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26326      *                        Roo.resizable.
26327      */
26328     resizable : false,
26329      /**
26330      * @cfg {Number} height (in pixels)
26331      */   
26332     height: 300,
26333    /**
26334      * @cfg {Number} width (in pixels)
26335      */   
26336     width: false,
26337     
26338     /**
26339      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26340      * 
26341      */
26342     stylesheets: false,
26343     
26344     // id of frame..
26345     frameId: false,
26346     
26347     // private properties
26348     validationEvent : false,
26349     deferHeight: true,
26350     initialized : false,
26351     activated : false,
26352     
26353     onFocus : Roo.emptyFn,
26354     iframePad:3,
26355     hideMode:'offsets',
26356     
26357     tbContainer : false,
26358     
26359     bodyCls : '',
26360     
26361     toolbarContainer :function() {
26362         return this.wrap.select('.x-html-editor-tb',true).first();
26363     },
26364
26365     /**
26366      * Protected method that will not generally be called directly. It
26367      * is called when the editor creates its toolbar. Override this method if you need to
26368      * add custom toolbar buttons.
26369      * @param {HtmlEditor} editor
26370      */
26371     createToolbar : function(){
26372         Roo.log('renewing');
26373         Roo.log("create toolbars");
26374         
26375         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26376         this.toolbars[0].render(this.toolbarContainer());
26377         
26378         return;
26379         
26380 //        if (!editor.toolbars || !editor.toolbars.length) {
26381 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26382 //        }
26383 //        
26384 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26385 //            editor.toolbars[i] = Roo.factory(
26386 //                    typeof(editor.toolbars[i]) == 'string' ?
26387 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26388 //                Roo.bootstrap.HtmlEditor);
26389 //            editor.toolbars[i].init(editor);
26390 //        }
26391     },
26392
26393      
26394     // private
26395     onRender : function(ct, position)
26396     {
26397        // Roo.log("Call onRender: " + this.xtype);
26398         var _t = this;
26399         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26400       
26401         this.wrap = this.inputEl().wrap({
26402             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26403         });
26404         
26405         this.editorcore.onRender(ct, position);
26406          
26407         if (this.resizable) {
26408             this.resizeEl = new Roo.Resizable(this.wrap, {
26409                 pinned : true,
26410                 wrap: true,
26411                 dynamic : true,
26412                 minHeight : this.height,
26413                 height: this.height,
26414                 handles : this.resizable,
26415                 width: this.width,
26416                 listeners : {
26417                     resize : function(r, w, h) {
26418                         _t.onResize(w,h); // -something
26419                     }
26420                 }
26421             });
26422             
26423         }
26424         this.createToolbar(this);
26425        
26426         
26427         if(!this.width && this.resizable){
26428             this.setSize(this.wrap.getSize());
26429         }
26430         if (this.resizeEl) {
26431             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26432             // should trigger onReize..
26433         }
26434         
26435     },
26436
26437     // private
26438     onResize : function(w, h)
26439     {
26440         Roo.log('resize: ' +w + ',' + h );
26441         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26442         var ew = false;
26443         var eh = false;
26444         
26445         if(this.inputEl() ){
26446             if(typeof w == 'number'){
26447                 var aw = w - this.wrap.getFrameWidth('lr');
26448                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26449                 ew = aw;
26450             }
26451             if(typeof h == 'number'){
26452                  var tbh = -11;  // fixme it needs to tool bar size!
26453                 for (var i =0; i < this.toolbars.length;i++) {
26454                     // fixme - ask toolbars for heights?
26455                     tbh += this.toolbars[i].el.getHeight();
26456                     //if (this.toolbars[i].footer) {
26457                     //    tbh += this.toolbars[i].footer.el.getHeight();
26458                     //}
26459                 }
26460               
26461                 
26462                 
26463                 
26464                 
26465                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26466                 ah -= 5; // knock a few pixes off for look..
26467                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26468                 var eh = ah;
26469             }
26470         }
26471         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26472         this.editorcore.onResize(ew,eh);
26473         
26474     },
26475
26476     /**
26477      * Toggles the editor between standard and source edit mode.
26478      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26479      */
26480     toggleSourceEdit : function(sourceEditMode)
26481     {
26482         this.editorcore.toggleSourceEdit(sourceEditMode);
26483         
26484         if(this.editorcore.sourceEditMode){
26485             Roo.log('editor - showing textarea');
26486             
26487 //            Roo.log('in');
26488 //            Roo.log(this.syncValue());
26489             this.syncValue();
26490             this.inputEl().removeClass(['hide', 'x-hidden']);
26491             this.inputEl().dom.removeAttribute('tabIndex');
26492             this.inputEl().focus();
26493         }else{
26494             Roo.log('editor - hiding textarea');
26495 //            Roo.log('out')
26496 //            Roo.log(this.pushValue()); 
26497             this.pushValue();
26498             
26499             this.inputEl().addClass(['hide', 'x-hidden']);
26500             this.inputEl().dom.setAttribute('tabIndex', -1);
26501             //this.deferFocus();
26502         }
26503          
26504         if(this.resizable){
26505             this.setSize(this.wrap.getSize());
26506         }
26507         
26508         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26509     },
26510  
26511     // private (for BoxComponent)
26512     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26513
26514     // private (for BoxComponent)
26515     getResizeEl : function(){
26516         return this.wrap;
26517     },
26518
26519     // private (for BoxComponent)
26520     getPositionEl : function(){
26521         return this.wrap;
26522     },
26523
26524     // private
26525     initEvents : function(){
26526         this.originalValue = this.getValue();
26527     },
26528
26529 //    /**
26530 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26531 //     * @method
26532 //     */
26533 //    markInvalid : Roo.emptyFn,
26534 //    /**
26535 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26536 //     * @method
26537 //     */
26538 //    clearInvalid : Roo.emptyFn,
26539
26540     setValue : function(v){
26541         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26542         this.editorcore.pushValue();
26543     },
26544
26545      
26546     // private
26547     deferFocus : function(){
26548         this.focus.defer(10, this);
26549     },
26550
26551     // doc'ed in Field
26552     focus : function(){
26553         this.editorcore.focus();
26554         
26555     },
26556       
26557
26558     // private
26559     onDestroy : function(){
26560         
26561         
26562         
26563         if(this.rendered){
26564             
26565             for (var i =0; i < this.toolbars.length;i++) {
26566                 // fixme - ask toolbars for heights?
26567                 this.toolbars[i].onDestroy();
26568             }
26569             
26570             this.wrap.dom.innerHTML = '';
26571             this.wrap.remove();
26572         }
26573     },
26574
26575     // private
26576     onFirstFocus : function(){
26577         //Roo.log("onFirstFocus");
26578         this.editorcore.onFirstFocus();
26579          for (var i =0; i < this.toolbars.length;i++) {
26580             this.toolbars[i].onFirstFocus();
26581         }
26582         
26583     },
26584     
26585     // private
26586     syncValue : function()
26587     {   
26588         this.editorcore.syncValue();
26589     },
26590     
26591     pushValue : function()
26592     {   
26593         this.editorcore.pushValue();
26594     }
26595      
26596     
26597     // hide stuff that is not compatible
26598     /**
26599      * @event blur
26600      * @hide
26601      */
26602     /**
26603      * @event change
26604      * @hide
26605      */
26606     /**
26607      * @event focus
26608      * @hide
26609      */
26610     /**
26611      * @event specialkey
26612      * @hide
26613      */
26614     /**
26615      * @cfg {String} fieldClass @hide
26616      */
26617     /**
26618      * @cfg {String} focusClass @hide
26619      */
26620     /**
26621      * @cfg {String} autoCreate @hide
26622      */
26623     /**
26624      * @cfg {String} inputType @hide
26625      */
26626      
26627     /**
26628      * @cfg {String} invalidText @hide
26629      */
26630     /**
26631      * @cfg {String} msgFx @hide
26632      */
26633     /**
26634      * @cfg {String} validateOnBlur @hide
26635      */
26636 });
26637  
26638     
26639    
26640    
26641    
26642       
26643 Roo.namespace('Roo.bootstrap.htmleditor');
26644 /**
26645  * @class Roo.bootstrap.HtmlEditorToolbar1
26646  * Basic Toolbar
26647  * 
26648  * @example
26649  * Usage:
26650  *
26651  new Roo.bootstrap.HtmlEditor({
26652     ....
26653     toolbars : [
26654         new Roo.bootstrap.HtmlEditorToolbar1({
26655             disable : { fonts: 1 , format: 1, ..., ... , ...],
26656             btns : [ .... ]
26657         })
26658     }
26659      
26660  * 
26661  * @cfg {Object} disable List of elements to disable..
26662  * @cfg {Array} btns List of additional buttons.
26663  * 
26664  * 
26665  * NEEDS Extra CSS? 
26666  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26667  */
26668  
26669 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26670 {
26671     
26672     Roo.apply(this, config);
26673     
26674     // default disabled, based on 'good practice'..
26675     this.disable = this.disable || {};
26676     Roo.applyIf(this.disable, {
26677         fontSize : true,
26678         colors : true,
26679         specialElements : true
26680     });
26681     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26682     
26683     this.editor = config.editor;
26684     this.editorcore = config.editor.editorcore;
26685     
26686     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26687     
26688     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26689     // dont call parent... till later.
26690 }
26691 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26692      
26693     bar : true,
26694     
26695     editor : false,
26696     editorcore : false,
26697     
26698     
26699     formats : [
26700         "p" ,  
26701         "h1","h2","h3","h4","h5","h6", 
26702         "pre", "code", 
26703         "abbr", "acronym", "address", "cite", "samp", "var",
26704         'div','span'
26705     ],
26706     
26707     onRender : function(ct, position)
26708     {
26709        // Roo.log("Call onRender: " + this.xtype);
26710         
26711        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26712        Roo.log(this.el);
26713        this.el.dom.style.marginBottom = '0';
26714        var _this = this;
26715        var editorcore = this.editorcore;
26716        var editor= this.editor;
26717        
26718        var children = [];
26719        var btn = function(id,cmd , toggle, handler, html){
26720        
26721             var  event = toggle ? 'toggle' : 'click';
26722        
26723             var a = {
26724                 size : 'sm',
26725                 xtype: 'Button',
26726                 xns: Roo.bootstrap,
26727                 //glyphicon : id,
26728                 fa: id,
26729                 cmd : id || cmd,
26730                 enableToggle:toggle !== false,
26731                 html : html || '',
26732                 pressed : toggle ? false : null,
26733                 listeners : {}
26734             };
26735             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26736                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26737             };
26738             children.push(a);
26739             return a;
26740        }
26741        
26742     //    var cb_box = function...
26743         
26744         var style = {
26745                 xtype: 'Button',
26746                 size : 'sm',
26747                 xns: Roo.bootstrap,
26748                 fa : 'font',
26749                 //html : 'submit'
26750                 menu : {
26751                     xtype: 'Menu',
26752                     xns: Roo.bootstrap,
26753                     items:  []
26754                 }
26755         };
26756         Roo.each(this.formats, function(f) {
26757             style.menu.items.push({
26758                 xtype :'MenuItem',
26759                 xns: Roo.bootstrap,
26760                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26761                 tagname : f,
26762                 listeners : {
26763                     click : function()
26764                     {
26765                         editorcore.insertTag(this.tagname);
26766                         editor.focus();
26767                     }
26768                 }
26769                 
26770             });
26771         });
26772         children.push(style);   
26773         
26774         btn('bold',false,true);
26775         btn('italic',false,true);
26776         btn('align-left', 'justifyleft',true);
26777         btn('align-center', 'justifycenter',true);
26778         btn('align-right' , 'justifyright',true);
26779         btn('link', false, false, function(btn) {
26780             //Roo.log("create link?");
26781             var url = prompt(this.createLinkText, this.defaultLinkValue);
26782             if(url && url != 'http:/'+'/'){
26783                 this.editorcore.relayCmd('createlink', url);
26784             }
26785         }),
26786         btn('list','insertunorderedlist',true);
26787         btn('pencil', false,true, function(btn){
26788                 Roo.log(this);
26789                 this.toggleSourceEdit(btn.pressed);
26790         });
26791         
26792         if (this.editor.btns.length > 0) {
26793             for (var i = 0; i<this.editor.btns.length; i++) {
26794                 children.push(this.editor.btns[i]);
26795             }
26796         }
26797         
26798         /*
26799         var cog = {
26800                 xtype: 'Button',
26801                 size : 'sm',
26802                 xns: Roo.bootstrap,
26803                 glyphicon : 'cog',
26804                 //html : 'submit'
26805                 menu : {
26806                     xtype: 'Menu',
26807                     xns: Roo.bootstrap,
26808                     items:  []
26809                 }
26810         };
26811         
26812         cog.menu.items.push({
26813             xtype :'MenuItem',
26814             xns: Roo.bootstrap,
26815             html : Clean styles,
26816             tagname : f,
26817             listeners : {
26818                 click : function()
26819                 {
26820                     editorcore.insertTag(this.tagname);
26821                     editor.focus();
26822                 }
26823             }
26824             
26825         });
26826        */
26827         
26828          
26829        this.xtype = 'NavSimplebar';
26830         
26831         for(var i=0;i< children.length;i++) {
26832             
26833             this.buttons.add(this.addxtypeChild(children[i]));
26834             
26835         }
26836         
26837         editor.on('editorevent', this.updateToolbar, this);
26838     },
26839     onBtnClick : function(id)
26840     {
26841        this.editorcore.relayCmd(id);
26842        this.editorcore.focus();
26843     },
26844     
26845     /**
26846      * Protected method that will not generally be called directly. It triggers
26847      * a toolbar update by reading the markup state of the current selection in the editor.
26848      */
26849     updateToolbar: function(){
26850
26851         if(!this.editorcore.activated){
26852             this.editor.onFirstFocus(); // is this neeed?
26853             return;
26854         }
26855
26856         var btns = this.buttons; 
26857         var doc = this.editorcore.doc;
26858         btns.get('bold').setActive(doc.queryCommandState('bold'));
26859         btns.get('italic').setActive(doc.queryCommandState('italic'));
26860         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26861         
26862         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26863         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26864         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26865         
26866         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26867         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26868          /*
26869         
26870         var ans = this.editorcore.getAllAncestors();
26871         if (this.formatCombo) {
26872             
26873             
26874             var store = this.formatCombo.store;
26875             this.formatCombo.setValue("");
26876             for (var i =0; i < ans.length;i++) {
26877                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26878                     // select it..
26879                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26880                     break;
26881                 }
26882             }
26883         }
26884         
26885         
26886         
26887         // hides menus... - so this cant be on a menu...
26888         Roo.bootstrap.MenuMgr.hideAll();
26889         */
26890         Roo.bootstrap.MenuMgr.hideAll();
26891         //this.editorsyncValue();
26892     },
26893     onFirstFocus: function() {
26894         this.buttons.each(function(item){
26895            item.enable();
26896         });
26897     },
26898     toggleSourceEdit : function(sourceEditMode){
26899         
26900           
26901         if(sourceEditMode){
26902             Roo.log("disabling buttons");
26903            this.buttons.each( function(item){
26904                 if(item.cmd != 'pencil'){
26905                     item.disable();
26906                 }
26907             });
26908           
26909         }else{
26910             Roo.log("enabling buttons");
26911             if(this.editorcore.initialized){
26912                 this.buttons.each( function(item){
26913                     item.enable();
26914                 });
26915             }
26916             
26917         }
26918         Roo.log("calling toggole on editor");
26919         // tell the editor that it's been pressed..
26920         this.editor.toggleSourceEdit(sourceEditMode);
26921        
26922     }
26923 });
26924
26925
26926
26927
26928  
26929 /*
26930  * - LGPL
26931  */
26932
26933 /**
26934  * @class Roo.bootstrap.Markdown
26935  * @extends Roo.bootstrap.TextArea
26936  * Bootstrap Showdown editable area
26937  * @cfg {string} content
26938  * 
26939  * @constructor
26940  * Create a new Showdown
26941  */
26942
26943 Roo.bootstrap.Markdown = function(config){
26944     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26945    
26946 };
26947
26948 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26949     
26950     editing :false,
26951     
26952     initEvents : function()
26953     {
26954         
26955         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26956         this.markdownEl = this.el.createChild({
26957             cls : 'roo-markdown-area'
26958         });
26959         this.inputEl().addClass('d-none');
26960         if (this.getValue() == '') {
26961             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26962             
26963         } else {
26964             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26965         }
26966         this.markdownEl.on('click', this.toggleTextEdit, this);
26967         this.on('blur', this.toggleTextEdit, this);
26968         this.on('specialkey', this.resizeTextArea, this);
26969     },
26970     
26971     toggleTextEdit : function()
26972     {
26973         var sh = this.markdownEl.getHeight();
26974         this.inputEl().addClass('d-none');
26975         this.markdownEl.addClass('d-none');
26976         if (!this.editing) {
26977             // show editor?
26978             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26979             this.inputEl().removeClass('d-none');
26980             this.inputEl().focus();
26981             this.editing = true;
26982             return;
26983         }
26984         // show showdown...
26985         this.updateMarkdown();
26986         this.markdownEl.removeClass('d-none');
26987         this.editing = false;
26988         return;
26989     },
26990     updateMarkdown : function()
26991     {
26992         if (this.getValue() == '') {
26993             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26994             return;
26995         }
26996  
26997         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26998     },
26999     
27000     resizeTextArea: function () {
27001         
27002         var sh = 100;
27003         Roo.log([sh, this.getValue().split("\n").length * 30]);
27004         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27005     },
27006     setValue : function(val)
27007     {
27008         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27009         if (!this.editing) {
27010             this.updateMarkdown();
27011         }
27012         
27013     },
27014     focus : function()
27015     {
27016         if (!this.editing) {
27017             this.toggleTextEdit();
27018         }
27019         
27020     }
27021
27022
27023 });
27024 /**
27025  * @class Roo.bootstrap.Table.AbstractSelectionModel
27026  * @extends Roo.util.Observable
27027  * Abstract base class for grid SelectionModels.  It provides the interface that should be
27028  * implemented by descendant classes.  This class should not be directly instantiated.
27029  * @constructor
27030  */
27031 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27032     this.locked = false;
27033     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27034 };
27035
27036
27037 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27038     /** @ignore Called by the grid automatically. Do not call directly. */
27039     init : function(grid){
27040         this.grid = grid;
27041         this.initEvents();
27042     },
27043
27044     /**
27045      * Locks the selections.
27046      */
27047     lock : function(){
27048         this.locked = true;
27049     },
27050
27051     /**
27052      * Unlocks the selections.
27053      */
27054     unlock : function(){
27055         this.locked = false;
27056     },
27057
27058     /**
27059      * Returns true if the selections are locked.
27060      * @return {Boolean}
27061      */
27062     isLocked : function(){
27063         return this.locked;
27064     },
27065     
27066     
27067     initEvents : function ()
27068     {
27069         
27070     }
27071 });
27072 /**
27073  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27074  * @class Roo.bootstrap.Table.RowSelectionModel
27075  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27076  * It supports multiple selections and keyboard selection/navigation. 
27077  * @constructor
27078  * @param {Object} config
27079  */
27080
27081 Roo.bootstrap.Table.RowSelectionModel = function(config){
27082     Roo.apply(this, config);
27083     this.selections = new Roo.util.MixedCollection(false, function(o){
27084         return o.id;
27085     });
27086
27087     this.last = false;
27088     this.lastActive = false;
27089
27090     this.addEvents({
27091         /**
27092              * @event selectionchange
27093              * Fires when the selection changes
27094              * @param {SelectionModel} this
27095              */
27096             "selectionchange" : true,
27097         /**
27098              * @event afterselectionchange
27099              * Fires after the selection changes (eg. by key press or clicking)
27100              * @param {SelectionModel} this
27101              */
27102             "afterselectionchange" : true,
27103         /**
27104              * @event beforerowselect
27105              * Fires when a row is selected being selected, return false to cancel.
27106              * @param {SelectionModel} this
27107              * @param {Number} rowIndex The selected index
27108              * @param {Boolean} keepExisting False if other selections will be cleared
27109              */
27110             "beforerowselect" : true,
27111         /**
27112              * @event rowselect
27113              * Fires when a row is selected.
27114              * @param {SelectionModel} this
27115              * @param {Number} rowIndex The selected index
27116              * @param {Roo.data.Record} r The record
27117              */
27118             "rowselect" : true,
27119         /**
27120              * @event rowdeselect
27121              * Fires when a row is deselected.
27122              * @param {SelectionModel} this
27123              * @param {Number} rowIndex The selected index
27124              */
27125         "rowdeselect" : true
27126     });
27127     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27128     this.locked = false;
27129  };
27130
27131 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27132     /**
27133      * @cfg {Boolean} singleSelect
27134      * True to allow selection of only one row at a time (defaults to false)
27135      */
27136     singleSelect : false,
27137
27138     // private
27139     initEvents : function()
27140     {
27141
27142         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27143         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27144         //}else{ // allow click to work like normal
27145          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27146         //}
27147         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27148         this.grid.on("rowclick", this.handleMouseDown, this);
27149         
27150         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27151             "up" : function(e){
27152                 if(!e.shiftKey){
27153                     this.selectPrevious(e.shiftKey);
27154                 }else if(this.last !== false && this.lastActive !== false){
27155                     var last = this.last;
27156                     this.selectRange(this.last,  this.lastActive-1);
27157                     this.grid.getView().focusRow(this.lastActive);
27158                     if(last !== false){
27159                         this.last = last;
27160                     }
27161                 }else{
27162                     this.selectFirstRow();
27163                 }
27164                 this.fireEvent("afterselectionchange", this);
27165             },
27166             "down" : function(e){
27167                 if(!e.shiftKey){
27168                     this.selectNext(e.shiftKey);
27169                 }else if(this.last !== false && this.lastActive !== false){
27170                     var last = this.last;
27171                     this.selectRange(this.last,  this.lastActive+1);
27172                     this.grid.getView().focusRow(this.lastActive);
27173                     if(last !== false){
27174                         this.last = last;
27175                     }
27176                 }else{
27177                     this.selectFirstRow();
27178                 }
27179                 this.fireEvent("afterselectionchange", this);
27180             },
27181             scope: this
27182         });
27183         this.grid.store.on('load', function(){
27184             this.selections.clear();
27185         },this);
27186         /*
27187         var view = this.grid.view;
27188         view.on("refresh", this.onRefresh, this);
27189         view.on("rowupdated", this.onRowUpdated, this);
27190         view.on("rowremoved", this.onRemove, this);
27191         */
27192     },
27193
27194     // private
27195     onRefresh : function()
27196     {
27197         var ds = this.grid.store, i, v = this.grid.view;
27198         var s = this.selections;
27199         s.each(function(r){
27200             if((i = ds.indexOfId(r.id)) != -1){
27201                 v.onRowSelect(i);
27202             }else{
27203                 s.remove(r);
27204             }
27205         });
27206     },
27207
27208     // private
27209     onRemove : function(v, index, r){
27210         this.selections.remove(r);
27211     },
27212
27213     // private
27214     onRowUpdated : function(v, index, r){
27215         if(this.isSelected(r)){
27216             v.onRowSelect(index);
27217         }
27218     },
27219
27220     /**
27221      * Select records.
27222      * @param {Array} records The records to select
27223      * @param {Boolean} keepExisting (optional) True to keep existing selections
27224      */
27225     selectRecords : function(records, keepExisting)
27226     {
27227         if(!keepExisting){
27228             this.clearSelections();
27229         }
27230             var ds = this.grid.store;
27231         for(var i = 0, len = records.length; i < len; i++){
27232             this.selectRow(ds.indexOf(records[i]), true);
27233         }
27234     },
27235
27236     /**
27237      * Gets the number of selected rows.
27238      * @return {Number}
27239      */
27240     getCount : function(){
27241         return this.selections.length;
27242     },
27243
27244     /**
27245      * Selects the first row in the grid.
27246      */
27247     selectFirstRow : function(){
27248         this.selectRow(0);
27249     },
27250
27251     /**
27252      * Select the last row.
27253      * @param {Boolean} keepExisting (optional) True to keep existing selections
27254      */
27255     selectLastRow : function(keepExisting){
27256         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27257         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27258     },
27259
27260     /**
27261      * Selects the row immediately following the last selected row.
27262      * @param {Boolean} keepExisting (optional) True to keep existing selections
27263      */
27264     selectNext : function(keepExisting)
27265     {
27266             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27267             this.selectRow(this.last+1, keepExisting);
27268             this.grid.getView().focusRow(this.last);
27269         }
27270     },
27271
27272     /**
27273      * Selects the row that precedes the last selected row.
27274      * @param {Boolean} keepExisting (optional) True to keep existing selections
27275      */
27276     selectPrevious : function(keepExisting){
27277         if(this.last){
27278             this.selectRow(this.last-1, keepExisting);
27279             this.grid.getView().focusRow(this.last);
27280         }
27281     },
27282
27283     /**
27284      * Returns the selected records
27285      * @return {Array} Array of selected records
27286      */
27287     getSelections : function(){
27288         return [].concat(this.selections.items);
27289     },
27290
27291     /**
27292      * Returns the first selected record.
27293      * @return {Record}
27294      */
27295     getSelected : function(){
27296         return this.selections.itemAt(0);
27297     },
27298
27299
27300     /**
27301      * Clears all selections.
27302      */
27303     clearSelections : function(fast)
27304     {
27305         if(this.locked) {
27306             return;
27307         }
27308         if(fast !== true){
27309                 var ds = this.grid.store;
27310             var s = this.selections;
27311             s.each(function(r){
27312                 this.deselectRow(ds.indexOfId(r.id));
27313             }, this);
27314             s.clear();
27315         }else{
27316             this.selections.clear();
27317         }
27318         this.last = false;
27319     },
27320
27321
27322     /**
27323      * Selects all rows.
27324      */
27325     selectAll : function(){
27326         if(this.locked) {
27327             return;
27328         }
27329         this.selections.clear();
27330         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27331             this.selectRow(i, true);
27332         }
27333     },
27334
27335     /**
27336      * Returns True if there is a selection.
27337      * @return {Boolean}
27338      */
27339     hasSelection : function(){
27340         return this.selections.length > 0;
27341     },
27342
27343     /**
27344      * Returns True if the specified row is selected.
27345      * @param {Number/Record} record The record or index of the record to check
27346      * @return {Boolean}
27347      */
27348     isSelected : function(index){
27349             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27350         return (r && this.selections.key(r.id) ? true : false);
27351     },
27352
27353     /**
27354      * Returns True if the specified record id is selected.
27355      * @param {String} id The id of record to check
27356      * @return {Boolean}
27357      */
27358     isIdSelected : function(id){
27359         return (this.selections.key(id) ? true : false);
27360     },
27361
27362
27363     // private
27364     handleMouseDBClick : function(e, t){
27365         
27366     },
27367     // private
27368     handleMouseDown : function(e, t)
27369     {
27370             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27371         if(this.isLocked() || rowIndex < 0 ){
27372             return;
27373         };
27374         if(e.shiftKey && this.last !== false){
27375             var last = this.last;
27376             this.selectRange(last, rowIndex, e.ctrlKey);
27377             this.last = last; // reset the last
27378             t.focus();
27379     
27380         }else{
27381             var isSelected = this.isSelected(rowIndex);
27382             //Roo.log("select row:" + rowIndex);
27383             if(isSelected){
27384                 this.deselectRow(rowIndex);
27385             } else {
27386                         this.selectRow(rowIndex, true);
27387             }
27388     
27389             /*
27390                 if(e.button !== 0 && isSelected){
27391                 alert('rowIndex 2: ' + rowIndex);
27392                     view.focusRow(rowIndex);
27393                 }else if(e.ctrlKey && isSelected){
27394                     this.deselectRow(rowIndex);
27395                 }else if(!isSelected){
27396                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27397                     view.focusRow(rowIndex);
27398                 }
27399             */
27400         }
27401         this.fireEvent("afterselectionchange", this);
27402     },
27403     // private
27404     handleDragableRowClick :  function(grid, rowIndex, e) 
27405     {
27406         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27407             this.selectRow(rowIndex, false);
27408             grid.view.focusRow(rowIndex);
27409              this.fireEvent("afterselectionchange", this);
27410         }
27411     },
27412     
27413     /**
27414      * Selects multiple rows.
27415      * @param {Array} rows Array of the indexes of the row to select
27416      * @param {Boolean} keepExisting (optional) True to keep existing selections
27417      */
27418     selectRows : function(rows, keepExisting){
27419         if(!keepExisting){
27420             this.clearSelections();
27421         }
27422         for(var i = 0, len = rows.length; i < len; i++){
27423             this.selectRow(rows[i], true);
27424         }
27425     },
27426
27427     /**
27428      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27429      * @param {Number} startRow The index of the first row in the range
27430      * @param {Number} endRow The index of the last row in the range
27431      * @param {Boolean} keepExisting (optional) True to retain existing selections
27432      */
27433     selectRange : function(startRow, endRow, keepExisting){
27434         if(this.locked) {
27435             return;
27436         }
27437         if(!keepExisting){
27438             this.clearSelections();
27439         }
27440         if(startRow <= endRow){
27441             for(var i = startRow; i <= endRow; i++){
27442                 this.selectRow(i, true);
27443             }
27444         }else{
27445             for(var i = startRow; i >= endRow; i--){
27446                 this.selectRow(i, true);
27447             }
27448         }
27449     },
27450
27451     /**
27452      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27453      * @param {Number} startRow The index of the first row in the range
27454      * @param {Number} endRow The index of the last row in the range
27455      */
27456     deselectRange : function(startRow, endRow, preventViewNotify){
27457         if(this.locked) {
27458             return;
27459         }
27460         for(var i = startRow; i <= endRow; i++){
27461             this.deselectRow(i, preventViewNotify);
27462         }
27463     },
27464
27465     /**
27466      * Selects a row.
27467      * @param {Number} row The index of the row to select
27468      * @param {Boolean} keepExisting (optional) True to keep existing selections
27469      */
27470     selectRow : function(index, keepExisting, preventViewNotify)
27471     {
27472             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27473             return;
27474         }
27475         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27476             if(!keepExisting || this.singleSelect){
27477                 this.clearSelections();
27478             }
27479             
27480             var r = this.grid.store.getAt(index);
27481             //console.log('selectRow - record id :' + r.id);
27482             
27483             this.selections.add(r);
27484             this.last = this.lastActive = index;
27485             if(!preventViewNotify){
27486                 var proxy = new Roo.Element(
27487                                 this.grid.getRowDom(index)
27488                 );
27489                 proxy.addClass('bg-info info');
27490             }
27491             this.fireEvent("rowselect", this, index, r);
27492             this.fireEvent("selectionchange", this);
27493         }
27494     },
27495
27496     /**
27497      * Deselects a row.
27498      * @param {Number} row The index of the row to deselect
27499      */
27500     deselectRow : function(index, preventViewNotify)
27501     {
27502         if(this.locked) {
27503             return;
27504         }
27505         if(this.last == index){
27506             this.last = false;
27507         }
27508         if(this.lastActive == index){
27509             this.lastActive = false;
27510         }
27511         
27512         var r = this.grid.store.getAt(index);
27513         if (!r) {
27514             return;
27515         }
27516         
27517         this.selections.remove(r);
27518         //.console.log('deselectRow - record id :' + r.id);
27519         if(!preventViewNotify){
27520         
27521             var proxy = new Roo.Element(
27522                 this.grid.getRowDom(index)
27523             );
27524             proxy.removeClass('bg-info info');
27525         }
27526         this.fireEvent("rowdeselect", this, index);
27527         this.fireEvent("selectionchange", this);
27528     },
27529
27530     // private
27531     restoreLast : function(){
27532         if(this._last){
27533             this.last = this._last;
27534         }
27535     },
27536
27537     // private
27538     acceptsNav : function(row, col, cm){
27539         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27540     },
27541
27542     // private
27543     onEditorKey : function(field, e){
27544         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27545         if(k == e.TAB){
27546             e.stopEvent();
27547             ed.completeEdit();
27548             if(e.shiftKey){
27549                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27550             }else{
27551                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27552             }
27553         }else if(k == e.ENTER && !e.ctrlKey){
27554             e.stopEvent();
27555             ed.completeEdit();
27556             if(e.shiftKey){
27557                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27558             }else{
27559                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27560             }
27561         }else if(k == e.ESC){
27562             ed.cancelEdit();
27563         }
27564         if(newCell){
27565             g.startEditing(newCell[0], newCell[1]);
27566         }
27567     }
27568 });
27569 /*
27570  * Based on:
27571  * Ext JS Library 1.1.1
27572  * Copyright(c) 2006-2007, Ext JS, LLC.
27573  *
27574  * Originally Released Under LGPL - original licence link has changed is not relivant.
27575  *
27576  * Fork - LGPL
27577  * <script type="text/javascript">
27578  */
27579  
27580 /**
27581  * @class Roo.bootstrap.PagingToolbar
27582  * @extends Roo.bootstrap.NavSimplebar
27583  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27584  * @constructor
27585  * Create a new PagingToolbar
27586  * @param {Object} config The config object
27587  * @param {Roo.data.Store} store
27588  */
27589 Roo.bootstrap.PagingToolbar = function(config)
27590 {
27591     // old args format still supported... - xtype is prefered..
27592         // created from xtype...
27593     
27594     this.ds = config.dataSource;
27595     
27596     if (config.store && !this.ds) {
27597         this.store= Roo.factory(config.store, Roo.data);
27598         this.ds = this.store;
27599         this.ds.xmodule = this.xmodule || false;
27600     }
27601     
27602     this.toolbarItems = [];
27603     if (config.items) {
27604         this.toolbarItems = config.items;
27605     }
27606     
27607     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27608     
27609     this.cursor = 0;
27610     
27611     if (this.ds) { 
27612         this.bind(this.ds);
27613     }
27614     
27615     if (Roo.bootstrap.version == 4) {
27616         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27617     } else {
27618         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27619     }
27620     
27621 };
27622
27623 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27624     /**
27625      * @cfg {Roo.data.Store} dataSource
27626      * The underlying data store providing the paged data
27627      */
27628     /**
27629      * @cfg {String/HTMLElement/Element} container
27630      * container The id or element that will contain the toolbar
27631      */
27632     /**
27633      * @cfg {Boolean} displayInfo
27634      * True to display the displayMsg (defaults to false)
27635      */
27636     /**
27637      * @cfg {Number} pageSize
27638      * The number of records to display per page (defaults to 20)
27639      */
27640     pageSize: 20,
27641     /**
27642      * @cfg {String} displayMsg
27643      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27644      */
27645     displayMsg : 'Displaying {0} - {1} of {2}',
27646     /**
27647      * @cfg {String} emptyMsg
27648      * The message to display when no records are found (defaults to "No data to display")
27649      */
27650     emptyMsg : 'No data to display',
27651     /**
27652      * Customizable piece of the default paging text (defaults to "Page")
27653      * @type String
27654      */
27655     beforePageText : "Page",
27656     /**
27657      * Customizable piece of the default paging text (defaults to "of %0")
27658      * @type String
27659      */
27660     afterPageText : "of {0}",
27661     /**
27662      * Customizable piece of the default paging text (defaults to "First Page")
27663      * @type String
27664      */
27665     firstText : "First Page",
27666     /**
27667      * Customizable piece of the default paging text (defaults to "Previous Page")
27668      * @type String
27669      */
27670     prevText : "Previous Page",
27671     /**
27672      * Customizable piece of the default paging text (defaults to "Next Page")
27673      * @type String
27674      */
27675     nextText : "Next Page",
27676     /**
27677      * Customizable piece of the default paging text (defaults to "Last Page")
27678      * @type String
27679      */
27680     lastText : "Last Page",
27681     /**
27682      * Customizable piece of the default paging text (defaults to "Refresh")
27683      * @type String
27684      */
27685     refreshText : "Refresh",
27686
27687     buttons : false,
27688     // private
27689     onRender : function(ct, position) 
27690     {
27691         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27692         this.navgroup.parentId = this.id;
27693         this.navgroup.onRender(this.el, null);
27694         // add the buttons to the navgroup
27695         
27696         if(this.displayInfo){
27697             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27698             this.displayEl = this.el.select('.x-paging-info', true).first();
27699 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27700 //            this.displayEl = navel.el.select('span',true).first();
27701         }
27702         
27703         var _this = this;
27704         
27705         if(this.buttons){
27706             Roo.each(_this.buttons, function(e){ // this might need to use render????
27707                Roo.factory(e).render(_this.el);
27708             });
27709         }
27710             
27711         Roo.each(_this.toolbarItems, function(e) {
27712             _this.navgroup.addItem(e);
27713         });
27714         
27715         
27716         this.first = this.navgroup.addItem({
27717             tooltip: this.firstText,
27718             cls: "prev btn-outline-secondary",
27719             html : ' <i class="fa fa-step-backward"></i>',
27720             disabled: true,
27721             preventDefault: true,
27722             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27723         });
27724         
27725         this.prev =  this.navgroup.addItem({
27726             tooltip: this.prevText,
27727             cls: "prev btn-outline-secondary",
27728             html : ' <i class="fa fa-backward"></i>',
27729             disabled: true,
27730             preventDefault: true,
27731             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27732         });
27733     //this.addSeparator();
27734         
27735         
27736         var field = this.navgroup.addItem( {
27737             tagtype : 'span',
27738             cls : 'x-paging-position  btn-outline-secondary',
27739              disabled: true,
27740             html : this.beforePageText  +
27741                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27742                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27743          } ); //?? escaped?
27744         
27745         this.field = field.el.select('input', true).first();
27746         this.field.on("keydown", this.onPagingKeydown, this);
27747         this.field.on("focus", function(){this.dom.select();});
27748     
27749     
27750         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27751         //this.field.setHeight(18);
27752         //this.addSeparator();
27753         this.next = this.navgroup.addItem({
27754             tooltip: this.nextText,
27755             cls: "next btn-outline-secondary",
27756             html : ' <i class="fa fa-forward"></i>',
27757             disabled: true,
27758             preventDefault: true,
27759             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27760         });
27761         this.last = this.navgroup.addItem({
27762             tooltip: this.lastText,
27763             html : ' <i class="fa fa-step-forward"></i>',
27764             cls: "next btn-outline-secondary",
27765             disabled: true,
27766             preventDefault: true,
27767             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27768         });
27769     //this.addSeparator();
27770         this.loading = this.navgroup.addItem({
27771             tooltip: this.refreshText,
27772             cls: "btn-outline-secondary",
27773             html : ' <i class="fa fa-refresh"></i>',
27774             preventDefault: true,
27775             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27776         });
27777         
27778     },
27779
27780     // private
27781     updateInfo : function(){
27782         if(this.displayEl){
27783             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27784             var msg = count == 0 ?
27785                 this.emptyMsg :
27786                 String.format(
27787                     this.displayMsg,
27788                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27789                 );
27790             this.displayEl.update(msg);
27791         }
27792     },
27793
27794     // private
27795     onLoad : function(ds, r, o)
27796     {
27797         this.cursor = o.params && o.params.start ? o.params.start : 0;
27798         
27799         var d = this.getPageData(),
27800             ap = d.activePage,
27801             ps = d.pages;
27802         
27803         
27804         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27805         this.field.dom.value = ap;
27806         this.first.setDisabled(ap == 1);
27807         this.prev.setDisabled(ap == 1);
27808         this.next.setDisabled(ap == ps);
27809         this.last.setDisabled(ap == ps);
27810         this.loading.enable();
27811         this.updateInfo();
27812     },
27813
27814     // private
27815     getPageData : function(){
27816         var total = this.ds.getTotalCount();
27817         return {
27818             total : total,
27819             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27820             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27821         };
27822     },
27823
27824     // private
27825     onLoadError : function(){
27826         this.loading.enable();
27827     },
27828
27829     // private
27830     onPagingKeydown : function(e){
27831         var k = e.getKey();
27832         var d = this.getPageData();
27833         if(k == e.RETURN){
27834             var v = this.field.dom.value, pageNum;
27835             if(!v || isNaN(pageNum = parseInt(v, 10))){
27836                 this.field.dom.value = d.activePage;
27837                 return;
27838             }
27839             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27840             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27841             e.stopEvent();
27842         }
27843         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))
27844         {
27845           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27846           this.field.dom.value = pageNum;
27847           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27848           e.stopEvent();
27849         }
27850         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27851         {
27852           var v = this.field.dom.value, pageNum; 
27853           var increment = (e.shiftKey) ? 10 : 1;
27854           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27855                 increment *= -1;
27856           }
27857           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27858             this.field.dom.value = d.activePage;
27859             return;
27860           }
27861           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27862           {
27863             this.field.dom.value = parseInt(v, 10) + increment;
27864             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27865             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27866           }
27867           e.stopEvent();
27868         }
27869     },
27870
27871     // private
27872     beforeLoad : function(){
27873         if(this.loading){
27874             this.loading.disable();
27875         }
27876     },
27877
27878     // private
27879     onClick : function(which){
27880         
27881         var ds = this.ds;
27882         if (!ds) {
27883             return;
27884         }
27885         
27886         switch(which){
27887             case "first":
27888                 ds.load({params:{start: 0, limit: this.pageSize}});
27889             break;
27890             case "prev":
27891                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27892             break;
27893             case "next":
27894                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27895             break;
27896             case "last":
27897                 var total = ds.getTotalCount();
27898                 var extra = total % this.pageSize;
27899                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27900                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27901             break;
27902             case "refresh":
27903                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27904             break;
27905         }
27906     },
27907
27908     /**
27909      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27910      * @param {Roo.data.Store} store The data store to unbind
27911      */
27912     unbind : function(ds){
27913         ds.un("beforeload", this.beforeLoad, this);
27914         ds.un("load", this.onLoad, this);
27915         ds.un("loadexception", this.onLoadError, this);
27916         ds.un("remove", this.updateInfo, this);
27917         ds.un("add", this.updateInfo, this);
27918         this.ds = undefined;
27919     },
27920
27921     /**
27922      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27923      * @param {Roo.data.Store} store The data store to bind
27924      */
27925     bind : function(ds){
27926         ds.on("beforeload", this.beforeLoad, this);
27927         ds.on("load", this.onLoad, this);
27928         ds.on("loadexception", this.onLoadError, this);
27929         ds.on("remove", this.updateInfo, this);
27930         ds.on("add", this.updateInfo, this);
27931         this.ds = ds;
27932     }
27933 });/*
27934  * - LGPL
27935  *
27936  * element
27937  * 
27938  */
27939
27940 /**
27941  * @class Roo.bootstrap.MessageBar
27942  * @extends Roo.bootstrap.Component
27943  * Bootstrap MessageBar class
27944  * @cfg {String} html contents of the MessageBar
27945  * @cfg {String} weight (info | success | warning | danger) default info
27946  * @cfg {String} beforeClass insert the bar before the given class
27947  * @cfg {Boolean} closable (true | false) default false
27948  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27949  * 
27950  * @constructor
27951  * Create a new Element
27952  * @param {Object} config The config object
27953  */
27954
27955 Roo.bootstrap.MessageBar = function(config){
27956     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27957 };
27958
27959 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27960     
27961     html: '',
27962     weight: 'info',
27963     closable: false,
27964     fixed: false,
27965     beforeClass: 'bootstrap-sticky-wrap',
27966     
27967     getAutoCreate : function(){
27968         
27969         var cfg = {
27970             tag: 'div',
27971             cls: 'alert alert-dismissable alert-' + this.weight,
27972             cn: [
27973                 {
27974                     tag: 'span',
27975                     cls: 'message',
27976                     html: this.html || ''
27977                 }
27978             ]
27979         };
27980         
27981         if(this.fixed){
27982             cfg.cls += ' alert-messages-fixed';
27983         }
27984         
27985         if(this.closable){
27986             cfg.cn.push({
27987                 tag: 'button',
27988                 cls: 'close',
27989                 html: 'x'
27990             });
27991         }
27992         
27993         return cfg;
27994     },
27995     
27996     onRender : function(ct, position)
27997     {
27998         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27999         
28000         if(!this.el){
28001             var cfg = Roo.apply({},  this.getAutoCreate());
28002             cfg.id = Roo.id();
28003             
28004             if (this.cls) {
28005                 cfg.cls += ' ' + this.cls;
28006             }
28007             if (this.style) {
28008                 cfg.style = this.style;
28009             }
28010             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28011             
28012             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28013         }
28014         
28015         this.el.select('>button.close').on('click', this.hide, this);
28016         
28017     },
28018     
28019     show : function()
28020     {
28021         if (!this.rendered) {
28022             this.render();
28023         }
28024         
28025         this.el.show();
28026         
28027         this.fireEvent('show', this);
28028         
28029     },
28030     
28031     hide : function()
28032     {
28033         if (!this.rendered) {
28034             this.render();
28035         }
28036         
28037         this.el.hide();
28038         
28039         this.fireEvent('hide', this);
28040     },
28041     
28042     update : function()
28043     {
28044 //        var e = this.el.dom.firstChild;
28045 //        
28046 //        if(this.closable){
28047 //            e = e.nextSibling;
28048 //        }
28049 //        
28050 //        e.data = this.html || '';
28051
28052         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28053     }
28054    
28055 });
28056
28057  
28058
28059      /*
28060  * - LGPL
28061  *
28062  * Graph
28063  * 
28064  */
28065
28066
28067 /**
28068  * @class Roo.bootstrap.Graph
28069  * @extends Roo.bootstrap.Component
28070  * Bootstrap Graph class
28071 > Prameters
28072  -sm {number} sm 4
28073  -md {number} md 5
28074  @cfg {String} graphtype  bar | vbar | pie
28075  @cfg {number} g_x coodinator | centre x (pie)
28076  @cfg {number} g_y coodinator | centre y (pie)
28077  @cfg {number} g_r radius (pie)
28078  @cfg {number} g_height height of the chart (respected by all elements in the set)
28079  @cfg {number} g_width width of the chart (respected by all elements in the set)
28080  @cfg {Object} title The title of the chart
28081     
28082  -{Array}  values
28083  -opts (object) options for the chart 
28084      o {
28085      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28086      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28087      o vgutter (number)
28088      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.
28089      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28090      o to
28091      o stretch (boolean)
28092      o }
28093  -opts (object) options for the pie
28094      o{
28095      o cut
28096      o startAngle (number)
28097      o endAngle (number)
28098      } 
28099  *
28100  * @constructor
28101  * Create a new Input
28102  * @param {Object} config The config object
28103  */
28104
28105 Roo.bootstrap.Graph = function(config){
28106     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28107     
28108     this.addEvents({
28109         // img events
28110         /**
28111          * @event click
28112          * The img click event for the img.
28113          * @param {Roo.EventObject} e
28114          */
28115         "click" : true
28116     });
28117 };
28118
28119 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28120     
28121     sm: 4,
28122     md: 5,
28123     graphtype: 'bar',
28124     g_height: 250,
28125     g_width: 400,
28126     g_x: 50,
28127     g_y: 50,
28128     g_r: 30,
28129     opts:{
28130         //g_colors: this.colors,
28131         g_type: 'soft',
28132         g_gutter: '20%'
28133
28134     },
28135     title : false,
28136
28137     getAutoCreate : function(){
28138         
28139         var cfg = {
28140             tag: 'div',
28141             html : null
28142         };
28143         
28144         
28145         return  cfg;
28146     },
28147
28148     onRender : function(ct,position){
28149         
28150         
28151         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28152         
28153         if (typeof(Raphael) == 'undefined') {
28154             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28155             return;
28156         }
28157         
28158         this.raphael = Raphael(this.el.dom);
28159         
28160                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28161                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28162                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28163                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28164                 /*
28165                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28166                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28167                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28168                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28169                 
28170                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28171                 r.barchart(330, 10, 300, 220, data1);
28172                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28173                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28174                 */
28175                 
28176                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28177                 // r.barchart(30, 30, 560, 250,  xdata, {
28178                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28179                 //     axis : "0 0 1 1",
28180                 //     axisxlabels :  xdata
28181                 //     //yvalues : cols,
28182                    
28183                 // });
28184 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28185 //        
28186 //        this.load(null,xdata,{
28187 //                axis : "0 0 1 1",
28188 //                axisxlabels :  xdata
28189 //                });
28190
28191     },
28192
28193     load : function(graphtype,xdata,opts)
28194     {
28195         this.raphael.clear();
28196         if(!graphtype) {
28197             graphtype = this.graphtype;
28198         }
28199         if(!opts){
28200             opts = this.opts;
28201         }
28202         var r = this.raphael,
28203             fin = function () {
28204                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28205             },
28206             fout = function () {
28207                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28208             },
28209             pfin = function() {
28210                 this.sector.stop();
28211                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28212
28213                 if (this.label) {
28214                     this.label[0].stop();
28215                     this.label[0].attr({ r: 7.5 });
28216                     this.label[1].attr({ "font-weight": 800 });
28217                 }
28218             },
28219             pfout = function() {
28220                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28221
28222                 if (this.label) {
28223                     this.label[0].animate({ r: 5 }, 500, "bounce");
28224                     this.label[1].attr({ "font-weight": 400 });
28225                 }
28226             };
28227
28228         switch(graphtype){
28229             case 'bar':
28230                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28231                 break;
28232             case 'hbar':
28233                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28234                 break;
28235             case 'pie':
28236 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28237 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28238 //            
28239                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28240                 
28241                 break;
28242
28243         }
28244         
28245         if(this.title){
28246             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28247         }
28248         
28249     },
28250     
28251     setTitle: function(o)
28252     {
28253         this.title = o;
28254     },
28255     
28256     initEvents: function() {
28257         
28258         if(!this.href){
28259             this.el.on('click', this.onClick, this);
28260         }
28261     },
28262     
28263     onClick : function(e)
28264     {
28265         Roo.log('img onclick');
28266         this.fireEvent('click', this, e);
28267     }
28268    
28269 });
28270
28271  
28272 /*
28273  * - LGPL
28274  *
28275  * numberBox
28276  * 
28277  */
28278 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28279
28280 /**
28281  * @class Roo.bootstrap.dash.NumberBox
28282  * @extends Roo.bootstrap.Component
28283  * Bootstrap NumberBox class
28284  * @cfg {String} headline Box headline
28285  * @cfg {String} content Box content
28286  * @cfg {String} icon Box icon
28287  * @cfg {String} footer Footer text
28288  * @cfg {String} fhref Footer href
28289  * 
28290  * @constructor
28291  * Create a new NumberBox
28292  * @param {Object} config The config object
28293  */
28294
28295
28296 Roo.bootstrap.dash.NumberBox = function(config){
28297     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28298     
28299 };
28300
28301 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28302     
28303     headline : '',
28304     content : '',
28305     icon : '',
28306     footer : '',
28307     fhref : '',
28308     ficon : '',
28309     
28310     getAutoCreate : function(){
28311         
28312         var cfg = {
28313             tag : 'div',
28314             cls : 'small-box ',
28315             cn : [
28316                 {
28317                     tag : 'div',
28318                     cls : 'inner',
28319                     cn :[
28320                         {
28321                             tag : 'h3',
28322                             cls : 'roo-headline',
28323                             html : this.headline
28324                         },
28325                         {
28326                             tag : 'p',
28327                             cls : 'roo-content',
28328                             html : this.content
28329                         }
28330                     ]
28331                 }
28332             ]
28333         };
28334         
28335         if(this.icon){
28336             cfg.cn.push({
28337                 tag : 'div',
28338                 cls : 'icon',
28339                 cn :[
28340                     {
28341                         tag : 'i',
28342                         cls : 'ion ' + this.icon
28343                     }
28344                 ]
28345             });
28346         }
28347         
28348         if(this.footer){
28349             var footer = {
28350                 tag : 'a',
28351                 cls : 'small-box-footer',
28352                 href : this.fhref || '#',
28353                 html : this.footer
28354             };
28355             
28356             cfg.cn.push(footer);
28357             
28358         }
28359         
28360         return  cfg;
28361     },
28362
28363     onRender : function(ct,position){
28364         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28365
28366
28367        
28368                 
28369     },
28370
28371     setHeadline: function (value)
28372     {
28373         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28374     },
28375     
28376     setFooter: function (value, href)
28377     {
28378         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28379         
28380         if(href){
28381             this.el.select('a.small-box-footer',true).first().attr('href', href);
28382         }
28383         
28384     },
28385
28386     setContent: function (value)
28387     {
28388         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28389     },
28390
28391     initEvents: function() 
28392     {   
28393         
28394     }
28395     
28396 });
28397
28398  
28399 /*
28400  * - LGPL
28401  *
28402  * TabBox
28403  * 
28404  */
28405 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28406
28407 /**
28408  * @class Roo.bootstrap.dash.TabBox
28409  * @extends Roo.bootstrap.Component
28410  * Bootstrap TabBox class
28411  * @cfg {String} title Title of the TabBox
28412  * @cfg {String} icon Icon of the TabBox
28413  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28414  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28415  * 
28416  * @constructor
28417  * Create a new TabBox
28418  * @param {Object} config The config object
28419  */
28420
28421
28422 Roo.bootstrap.dash.TabBox = function(config){
28423     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28424     this.addEvents({
28425         // raw events
28426         /**
28427          * @event addpane
28428          * When a pane is added
28429          * @param {Roo.bootstrap.dash.TabPane} pane
28430          */
28431         "addpane" : true,
28432         /**
28433          * @event activatepane
28434          * When a pane is activated
28435          * @param {Roo.bootstrap.dash.TabPane} pane
28436          */
28437         "activatepane" : true
28438         
28439          
28440     });
28441     
28442     this.panes = [];
28443 };
28444
28445 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28446
28447     title : '',
28448     icon : false,
28449     showtabs : true,
28450     tabScrollable : false,
28451     
28452     getChildContainer : function()
28453     {
28454         return this.el.select('.tab-content', true).first();
28455     },
28456     
28457     getAutoCreate : function(){
28458         
28459         var header = {
28460             tag: 'li',
28461             cls: 'pull-left header',
28462             html: this.title,
28463             cn : []
28464         };
28465         
28466         if(this.icon){
28467             header.cn.push({
28468                 tag: 'i',
28469                 cls: 'fa ' + this.icon
28470             });
28471         }
28472         
28473         var h = {
28474             tag: 'ul',
28475             cls: 'nav nav-tabs pull-right',
28476             cn: [
28477                 header
28478             ]
28479         };
28480         
28481         if(this.tabScrollable){
28482             h = {
28483                 tag: 'div',
28484                 cls: 'tab-header',
28485                 cn: [
28486                     {
28487                         tag: 'ul',
28488                         cls: 'nav nav-tabs pull-right',
28489                         cn: [
28490                             header
28491                         ]
28492                     }
28493                 ]
28494             };
28495         }
28496         
28497         var cfg = {
28498             tag: 'div',
28499             cls: 'nav-tabs-custom',
28500             cn: [
28501                 h,
28502                 {
28503                     tag: 'div',
28504                     cls: 'tab-content no-padding',
28505                     cn: []
28506                 }
28507             ]
28508         };
28509
28510         return  cfg;
28511     },
28512     initEvents : function()
28513     {
28514         //Roo.log('add add pane handler');
28515         this.on('addpane', this.onAddPane, this);
28516     },
28517      /**
28518      * Updates the box title
28519      * @param {String} html to set the title to.
28520      */
28521     setTitle : function(value)
28522     {
28523         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28524     },
28525     onAddPane : function(pane)
28526     {
28527         this.panes.push(pane);
28528         //Roo.log('addpane');
28529         //Roo.log(pane);
28530         // tabs are rendere left to right..
28531         if(!this.showtabs){
28532             return;
28533         }
28534         
28535         var ctr = this.el.select('.nav-tabs', true).first();
28536          
28537          
28538         var existing = ctr.select('.nav-tab',true);
28539         var qty = existing.getCount();;
28540         
28541         
28542         var tab = ctr.createChild({
28543             tag : 'li',
28544             cls : 'nav-tab' + (qty ? '' : ' active'),
28545             cn : [
28546                 {
28547                     tag : 'a',
28548                     href:'#',
28549                     html : pane.title
28550                 }
28551             ]
28552         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28553         pane.tab = tab;
28554         
28555         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28556         if (!qty) {
28557             pane.el.addClass('active');
28558         }
28559         
28560                 
28561     },
28562     onTabClick : function(ev,un,ob,pane)
28563     {
28564         //Roo.log('tab - prev default');
28565         ev.preventDefault();
28566         
28567         
28568         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28569         pane.tab.addClass('active');
28570         //Roo.log(pane.title);
28571         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28572         // technically we should have a deactivate event.. but maybe add later.
28573         // and it should not de-activate the selected tab...
28574         this.fireEvent('activatepane', pane);
28575         pane.el.addClass('active');
28576         pane.fireEvent('activate');
28577         
28578         
28579     },
28580     
28581     getActivePane : function()
28582     {
28583         var r = false;
28584         Roo.each(this.panes, function(p) {
28585             if(p.el.hasClass('active')){
28586                 r = p;
28587                 return false;
28588             }
28589             
28590             return;
28591         });
28592         
28593         return r;
28594     }
28595     
28596     
28597 });
28598
28599  
28600 /*
28601  * - LGPL
28602  *
28603  * Tab pane
28604  * 
28605  */
28606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28607 /**
28608  * @class Roo.bootstrap.TabPane
28609  * @extends Roo.bootstrap.Component
28610  * Bootstrap TabPane class
28611  * @cfg {Boolean} active (false | true) Default false
28612  * @cfg {String} title title of panel
28613
28614  * 
28615  * @constructor
28616  * Create a new TabPane
28617  * @param {Object} config The config object
28618  */
28619
28620 Roo.bootstrap.dash.TabPane = function(config){
28621     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28622     
28623     this.addEvents({
28624         // raw events
28625         /**
28626          * @event activate
28627          * When a pane is activated
28628          * @param {Roo.bootstrap.dash.TabPane} pane
28629          */
28630         "activate" : true
28631          
28632     });
28633 };
28634
28635 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28636     
28637     active : false,
28638     title : '',
28639     
28640     // the tabBox that this is attached to.
28641     tab : false,
28642      
28643     getAutoCreate : function() 
28644     {
28645         var cfg = {
28646             tag: 'div',
28647             cls: 'tab-pane'
28648         };
28649         
28650         if(this.active){
28651             cfg.cls += ' active';
28652         }
28653         
28654         return cfg;
28655     },
28656     initEvents  : function()
28657     {
28658         //Roo.log('trigger add pane handler');
28659         this.parent().fireEvent('addpane', this)
28660     },
28661     
28662      /**
28663      * Updates the tab title 
28664      * @param {String} html to set the title to.
28665      */
28666     setTitle: function(str)
28667     {
28668         if (!this.tab) {
28669             return;
28670         }
28671         this.title = str;
28672         this.tab.select('a', true).first().dom.innerHTML = str;
28673         
28674     }
28675     
28676     
28677     
28678 });
28679
28680  
28681
28682
28683  /*
28684  * - LGPL
28685  *
28686  * menu
28687  * 
28688  */
28689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28690
28691 /**
28692  * @class Roo.bootstrap.menu.Menu
28693  * @extends Roo.bootstrap.Component
28694  * Bootstrap Menu class - container for Menu
28695  * @cfg {String} html Text of the menu
28696  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28697  * @cfg {String} icon Font awesome icon
28698  * @cfg {String} pos Menu align to (top | bottom) default bottom
28699  * 
28700  * 
28701  * @constructor
28702  * Create a new Menu
28703  * @param {Object} config The config object
28704  */
28705
28706
28707 Roo.bootstrap.menu.Menu = function(config){
28708     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28709     
28710     this.addEvents({
28711         /**
28712          * @event beforeshow
28713          * Fires before this menu is displayed
28714          * @param {Roo.bootstrap.menu.Menu} this
28715          */
28716         beforeshow : true,
28717         /**
28718          * @event beforehide
28719          * Fires before this menu is hidden
28720          * @param {Roo.bootstrap.menu.Menu} this
28721          */
28722         beforehide : true,
28723         /**
28724          * @event show
28725          * Fires after this menu is displayed
28726          * @param {Roo.bootstrap.menu.Menu} this
28727          */
28728         show : true,
28729         /**
28730          * @event hide
28731          * Fires after this menu is hidden
28732          * @param {Roo.bootstrap.menu.Menu} this
28733          */
28734         hide : true,
28735         /**
28736          * @event click
28737          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28738          * @param {Roo.bootstrap.menu.Menu} this
28739          * @param {Roo.EventObject} e
28740          */
28741         click : true
28742     });
28743     
28744 };
28745
28746 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28747     
28748     submenu : false,
28749     html : '',
28750     weight : 'default',
28751     icon : false,
28752     pos : 'bottom',
28753     
28754     
28755     getChildContainer : function() {
28756         if(this.isSubMenu){
28757             return this.el;
28758         }
28759         
28760         return this.el.select('ul.dropdown-menu', true).first();  
28761     },
28762     
28763     getAutoCreate : function()
28764     {
28765         var text = [
28766             {
28767                 tag : 'span',
28768                 cls : 'roo-menu-text',
28769                 html : this.html
28770             }
28771         ];
28772         
28773         if(this.icon){
28774             text.unshift({
28775                 tag : 'i',
28776                 cls : 'fa ' + this.icon
28777             })
28778         }
28779         
28780         
28781         var cfg = {
28782             tag : 'div',
28783             cls : 'btn-group',
28784             cn : [
28785                 {
28786                     tag : 'button',
28787                     cls : 'dropdown-button btn btn-' + this.weight,
28788                     cn : text
28789                 },
28790                 {
28791                     tag : 'button',
28792                     cls : 'dropdown-toggle btn btn-' + this.weight,
28793                     cn : [
28794                         {
28795                             tag : 'span',
28796                             cls : 'caret'
28797                         }
28798                     ]
28799                 },
28800                 {
28801                     tag : 'ul',
28802                     cls : 'dropdown-menu'
28803                 }
28804             ]
28805             
28806         };
28807         
28808         if(this.pos == 'top'){
28809             cfg.cls += ' dropup';
28810         }
28811         
28812         if(this.isSubMenu){
28813             cfg = {
28814                 tag : 'ul',
28815                 cls : 'dropdown-menu'
28816             }
28817         }
28818         
28819         return cfg;
28820     },
28821     
28822     onRender : function(ct, position)
28823     {
28824         this.isSubMenu = ct.hasClass('dropdown-submenu');
28825         
28826         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28827     },
28828     
28829     initEvents : function() 
28830     {
28831         if(this.isSubMenu){
28832             return;
28833         }
28834         
28835         this.hidden = true;
28836         
28837         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28838         this.triggerEl.on('click', this.onTriggerPress, this);
28839         
28840         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28841         this.buttonEl.on('click', this.onClick, this);
28842         
28843     },
28844     
28845     list : function()
28846     {
28847         if(this.isSubMenu){
28848             return this.el;
28849         }
28850         
28851         return this.el.select('ul.dropdown-menu', true).first();
28852     },
28853     
28854     onClick : function(e)
28855     {
28856         this.fireEvent("click", this, e);
28857     },
28858     
28859     onTriggerPress  : function(e)
28860     {   
28861         if (this.isVisible()) {
28862             this.hide();
28863         } else {
28864             this.show();
28865         }
28866     },
28867     
28868     isVisible : function(){
28869         return !this.hidden;
28870     },
28871     
28872     show : function()
28873     {
28874         this.fireEvent("beforeshow", this);
28875         
28876         this.hidden = false;
28877         this.el.addClass('open');
28878         
28879         Roo.get(document).on("mouseup", this.onMouseUp, this);
28880         
28881         this.fireEvent("show", this);
28882         
28883         
28884     },
28885     
28886     hide : function()
28887     {
28888         this.fireEvent("beforehide", this);
28889         
28890         this.hidden = true;
28891         this.el.removeClass('open');
28892         
28893         Roo.get(document).un("mouseup", this.onMouseUp);
28894         
28895         this.fireEvent("hide", this);
28896     },
28897     
28898     onMouseUp : function()
28899     {
28900         this.hide();
28901     }
28902     
28903 });
28904
28905  
28906  /*
28907  * - LGPL
28908  *
28909  * menu item
28910  * 
28911  */
28912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28913
28914 /**
28915  * @class Roo.bootstrap.menu.Item
28916  * @extends Roo.bootstrap.Component
28917  * Bootstrap MenuItem class
28918  * @cfg {Boolean} submenu (true | false) default false
28919  * @cfg {String} html text of the item
28920  * @cfg {String} href the link
28921  * @cfg {Boolean} disable (true | false) default false
28922  * @cfg {Boolean} preventDefault (true | false) default true
28923  * @cfg {String} icon Font awesome icon
28924  * @cfg {String} pos Submenu align to (left | right) default right 
28925  * 
28926  * 
28927  * @constructor
28928  * Create a new Item
28929  * @param {Object} config The config object
28930  */
28931
28932
28933 Roo.bootstrap.menu.Item = function(config){
28934     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28935     this.addEvents({
28936         /**
28937          * @event mouseover
28938          * Fires when the mouse is hovering over this menu
28939          * @param {Roo.bootstrap.menu.Item} this
28940          * @param {Roo.EventObject} e
28941          */
28942         mouseover : true,
28943         /**
28944          * @event mouseout
28945          * Fires when the mouse exits this menu
28946          * @param {Roo.bootstrap.menu.Item} this
28947          * @param {Roo.EventObject} e
28948          */
28949         mouseout : true,
28950         // raw events
28951         /**
28952          * @event click
28953          * The raw click event for the entire grid.
28954          * @param {Roo.EventObject} e
28955          */
28956         click : true
28957     });
28958 };
28959
28960 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28961     
28962     submenu : false,
28963     href : '',
28964     html : '',
28965     preventDefault: true,
28966     disable : false,
28967     icon : false,
28968     pos : 'right',
28969     
28970     getAutoCreate : function()
28971     {
28972         var text = [
28973             {
28974                 tag : 'span',
28975                 cls : 'roo-menu-item-text',
28976                 html : this.html
28977             }
28978         ];
28979         
28980         if(this.icon){
28981             text.unshift({
28982                 tag : 'i',
28983                 cls : 'fa ' + this.icon
28984             })
28985         }
28986         
28987         var cfg = {
28988             tag : 'li',
28989             cn : [
28990                 {
28991                     tag : 'a',
28992                     href : this.href || '#',
28993                     cn : text
28994                 }
28995             ]
28996         };
28997         
28998         if(this.disable){
28999             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29000         }
29001         
29002         if(this.submenu){
29003             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29004             
29005             if(this.pos == 'left'){
29006                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29007             }
29008         }
29009         
29010         return cfg;
29011     },
29012     
29013     initEvents : function() 
29014     {
29015         this.el.on('mouseover', this.onMouseOver, this);
29016         this.el.on('mouseout', this.onMouseOut, this);
29017         
29018         this.el.select('a', true).first().on('click', this.onClick, this);
29019         
29020     },
29021     
29022     onClick : function(e)
29023     {
29024         if(this.preventDefault){
29025             e.preventDefault();
29026         }
29027         
29028         this.fireEvent("click", this, e);
29029     },
29030     
29031     onMouseOver : function(e)
29032     {
29033         if(this.submenu && this.pos == 'left'){
29034             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29035         }
29036         
29037         this.fireEvent("mouseover", this, e);
29038     },
29039     
29040     onMouseOut : function(e)
29041     {
29042         this.fireEvent("mouseout", this, e);
29043     }
29044 });
29045
29046  
29047
29048  /*
29049  * - LGPL
29050  *
29051  * menu separator
29052  * 
29053  */
29054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29055
29056 /**
29057  * @class Roo.bootstrap.menu.Separator
29058  * @extends Roo.bootstrap.Component
29059  * Bootstrap Separator class
29060  * 
29061  * @constructor
29062  * Create a new Separator
29063  * @param {Object} config The config object
29064  */
29065
29066
29067 Roo.bootstrap.menu.Separator = function(config){
29068     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29069 };
29070
29071 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29072     
29073     getAutoCreate : function(){
29074         var cfg = {
29075             tag : 'li',
29076             cls: 'dropdown-divider divider'
29077         };
29078         
29079         return cfg;
29080     }
29081    
29082 });
29083
29084  
29085
29086  /*
29087  * - LGPL
29088  *
29089  * Tooltip
29090  * 
29091  */
29092
29093 /**
29094  * @class Roo.bootstrap.Tooltip
29095  * Bootstrap Tooltip class
29096  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29097  * to determine which dom element triggers the tooltip.
29098  * 
29099  * It needs to add support for additional attributes like tooltip-position
29100  * 
29101  * @constructor
29102  * Create a new Toolti
29103  * @param {Object} config The config object
29104  */
29105
29106 Roo.bootstrap.Tooltip = function(config){
29107     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29108     
29109     this.alignment = Roo.bootstrap.Tooltip.alignment;
29110     
29111     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29112         this.alignment = config.alignment;
29113     }
29114     
29115 };
29116
29117 Roo.apply(Roo.bootstrap.Tooltip, {
29118     /**
29119      * @function init initialize tooltip monitoring.
29120      * @static
29121      */
29122     currentEl : false,
29123     currentTip : false,
29124     currentRegion : false,
29125     
29126     //  init : delay?
29127     
29128     init : function()
29129     {
29130         Roo.get(document).on('mouseover', this.enter ,this);
29131         Roo.get(document).on('mouseout', this.leave, this);
29132          
29133         
29134         this.currentTip = new Roo.bootstrap.Tooltip();
29135     },
29136     
29137     enter : function(ev)
29138     {
29139         var dom = ev.getTarget();
29140         
29141         //Roo.log(['enter',dom]);
29142         var el = Roo.fly(dom);
29143         if (this.currentEl) {
29144             //Roo.log(dom);
29145             //Roo.log(this.currentEl);
29146             //Roo.log(this.currentEl.contains(dom));
29147             if (this.currentEl == el) {
29148                 return;
29149             }
29150             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29151                 return;
29152             }
29153
29154         }
29155         
29156         if (this.currentTip.el) {
29157             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29158         }    
29159         //Roo.log(ev);
29160         
29161         if(!el || el.dom == document){
29162             return;
29163         }
29164         
29165         var bindEl = el; 
29166         var pel = false;
29167         if (!el.attr('tooltip')) {
29168             pel = el.findParent("[tooltip]");
29169             if (pel) {
29170                 bindEl = Roo.get(pel);
29171             }
29172         }
29173         
29174        
29175         
29176         // you can not look for children, as if el is the body.. then everythign is the child..
29177         if (!pel && !el.attr('tooltip')) { //
29178             if (!el.select("[tooltip]").elements.length) {
29179                 return;
29180             }
29181             // is the mouse over this child...?
29182             bindEl = el.select("[tooltip]").first();
29183             var xy = ev.getXY();
29184             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29185                 //Roo.log("not in region.");
29186                 return;
29187             }
29188             //Roo.log("child element over..");
29189             
29190         }
29191         this.currentEl = el;
29192         this.currentTip.bind(bindEl);
29193         this.currentRegion = Roo.lib.Region.getRegion(dom);
29194         this.currentTip.enter();
29195         
29196     },
29197     leave : function(ev)
29198     {
29199         var dom = ev.getTarget();
29200         //Roo.log(['leave',dom]);
29201         if (!this.currentEl) {
29202             return;
29203         }
29204         
29205         
29206         if (dom != this.currentEl.dom) {
29207             return;
29208         }
29209         var xy = ev.getXY();
29210         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29211             return;
29212         }
29213         // only activate leave if mouse cursor is outside... bounding box..
29214         
29215         
29216         
29217         
29218         if (this.currentTip) {
29219             this.currentTip.leave();
29220         }
29221         //Roo.log('clear currentEl');
29222         this.currentEl = false;
29223         
29224         
29225     },
29226     alignment : {
29227         'left' : ['r-l', [-2,0], 'right'],
29228         'right' : ['l-r', [2,0], 'left'],
29229         'bottom' : ['t-b', [0,2], 'top'],
29230         'top' : [ 'b-t', [0,-2], 'bottom']
29231     }
29232     
29233 });
29234
29235
29236 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29237     
29238     
29239     bindEl : false,
29240     
29241     delay : null, // can be { show : 300 , hide: 500}
29242     
29243     timeout : null,
29244     
29245     hoverState : null, //???
29246     
29247     placement : 'bottom', 
29248     
29249     alignment : false,
29250     
29251     getAutoCreate : function(){
29252     
29253         var cfg = {
29254            cls : 'tooltip',   
29255            role : 'tooltip',
29256            cn : [
29257                 {
29258                     cls : 'tooltip-arrow arrow'
29259                 },
29260                 {
29261                     cls : 'tooltip-inner'
29262                 }
29263            ]
29264         };
29265         
29266         return cfg;
29267     },
29268     bind : function(el)
29269     {
29270         this.bindEl = el;
29271     },
29272     
29273     initEvents : function()
29274     {
29275         this.arrowEl = this.el.select('.arrow', true).first();
29276         this.innerEl = this.el.select('.tooltip-inner', true).first();
29277     },
29278     
29279     enter : function () {
29280        
29281         if (this.timeout != null) {
29282             clearTimeout(this.timeout);
29283         }
29284         
29285         this.hoverState = 'in';
29286          //Roo.log("enter - show");
29287         if (!this.delay || !this.delay.show) {
29288             this.show();
29289             return;
29290         }
29291         var _t = this;
29292         this.timeout = setTimeout(function () {
29293             if (_t.hoverState == 'in') {
29294                 _t.show();
29295             }
29296         }, this.delay.show);
29297     },
29298     leave : function()
29299     {
29300         clearTimeout(this.timeout);
29301     
29302         this.hoverState = 'out';
29303          if (!this.delay || !this.delay.hide) {
29304             this.hide();
29305             return;
29306         }
29307        
29308         var _t = this;
29309         this.timeout = setTimeout(function () {
29310             //Roo.log("leave - timeout");
29311             
29312             if (_t.hoverState == 'out') {
29313                 _t.hide();
29314                 Roo.bootstrap.Tooltip.currentEl = false;
29315             }
29316         }, delay);
29317     },
29318     
29319     show : function (msg)
29320     {
29321         if (!this.el) {
29322             this.render(document.body);
29323         }
29324         // set content.
29325         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29326         
29327         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29328         
29329         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29330         
29331         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29332                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29333         
29334         var placement = typeof this.placement == 'function' ?
29335             this.placement.call(this, this.el, on_el) :
29336             this.placement;
29337             
29338         var autoToken = /\s?auto?\s?/i;
29339         var autoPlace = autoToken.test(placement);
29340         if (autoPlace) {
29341             placement = placement.replace(autoToken, '') || 'top';
29342         }
29343         
29344         //this.el.detach()
29345         //this.el.setXY([0,0]);
29346         this.el.show();
29347         //this.el.dom.style.display='block';
29348         
29349         //this.el.appendTo(on_el);
29350         
29351         var p = this.getPosition();
29352         var box = this.el.getBox();
29353         
29354         if (autoPlace) {
29355             // fixme..
29356         }
29357         
29358         var align = this.alignment[placement];
29359         
29360         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29361         
29362         if(placement == 'top' || placement == 'bottom'){
29363             if(xy[0] < 0){
29364                 placement = 'right';
29365             }
29366             
29367             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29368                 placement = 'left';
29369             }
29370             
29371             var scroll = Roo.select('body', true).first().getScroll();
29372             
29373             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29374                 placement = 'top';
29375             }
29376             
29377             align = this.alignment[placement];
29378             
29379             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29380             
29381         }
29382         
29383         var elems = document.getElementsByTagName('div');
29384         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29385         for (var i = 0; i < elems.length; i++) {
29386           var zindex = Number.parseInt(
29387                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29388                 10
29389           );
29390           if (zindex > highest) {
29391             highest = zindex;
29392           }
29393         }
29394         
29395         
29396         
29397         this.el.dom.style.zIndex = highest;
29398         
29399         this.el.alignTo(this.bindEl, align[0],align[1]);
29400         //var arrow = this.el.select('.arrow',true).first();
29401         //arrow.set(align[2], 
29402         
29403         this.el.addClass(placement);
29404         this.el.addClass("bs-tooltip-"+ placement);
29405         
29406         this.el.addClass('in fade show');
29407         
29408         this.hoverState = null;
29409         
29410         if (this.el.hasClass('fade')) {
29411             // fade it?
29412         }
29413         
29414         
29415         
29416         
29417         
29418     },
29419     hide : function()
29420     {
29421          
29422         if (!this.el) {
29423             return;
29424         }
29425         //this.el.setXY([0,0]);
29426         this.el.removeClass(['show', 'in']);
29427         //this.el.hide();
29428         
29429     }
29430     
29431 });
29432  
29433
29434  /*
29435  * - LGPL
29436  *
29437  * Location Picker
29438  * 
29439  */
29440
29441 /**
29442  * @class Roo.bootstrap.LocationPicker
29443  * @extends Roo.bootstrap.Component
29444  * Bootstrap LocationPicker class
29445  * @cfg {Number} latitude Position when init default 0
29446  * @cfg {Number} longitude Position when init default 0
29447  * @cfg {Number} zoom default 15
29448  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29449  * @cfg {Boolean} mapTypeControl default false
29450  * @cfg {Boolean} disableDoubleClickZoom default false
29451  * @cfg {Boolean} scrollwheel default true
29452  * @cfg {Boolean} streetViewControl default false
29453  * @cfg {Number} radius default 0
29454  * @cfg {String} locationName
29455  * @cfg {Boolean} draggable default true
29456  * @cfg {Boolean} enableAutocomplete default false
29457  * @cfg {Boolean} enableReverseGeocode default true
29458  * @cfg {String} markerTitle
29459  * 
29460  * @constructor
29461  * Create a new LocationPicker
29462  * @param {Object} config The config object
29463  */
29464
29465
29466 Roo.bootstrap.LocationPicker = function(config){
29467     
29468     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29469     
29470     this.addEvents({
29471         /**
29472          * @event initial
29473          * Fires when the picker initialized.
29474          * @param {Roo.bootstrap.LocationPicker} this
29475          * @param {Google Location} location
29476          */
29477         initial : true,
29478         /**
29479          * @event positionchanged
29480          * Fires when the picker position changed.
29481          * @param {Roo.bootstrap.LocationPicker} this
29482          * @param {Google Location} location
29483          */
29484         positionchanged : true,
29485         /**
29486          * @event resize
29487          * Fires when the map resize.
29488          * @param {Roo.bootstrap.LocationPicker} this
29489          */
29490         resize : true,
29491         /**
29492          * @event show
29493          * Fires when the map show.
29494          * @param {Roo.bootstrap.LocationPicker} this
29495          */
29496         show : true,
29497         /**
29498          * @event hide
29499          * Fires when the map hide.
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          */
29502         hide : true,
29503         /**
29504          * @event mapClick
29505          * Fires when click the map.
29506          * @param {Roo.bootstrap.LocationPicker} this
29507          * @param {Map event} e
29508          */
29509         mapClick : true,
29510         /**
29511          * @event mapRightClick
29512          * Fires when right click the map.
29513          * @param {Roo.bootstrap.LocationPicker} this
29514          * @param {Map event} e
29515          */
29516         mapRightClick : true,
29517         /**
29518          * @event markerClick
29519          * Fires when click the marker.
29520          * @param {Roo.bootstrap.LocationPicker} this
29521          * @param {Map event} e
29522          */
29523         markerClick : true,
29524         /**
29525          * @event markerRightClick
29526          * Fires when right click the marker.
29527          * @param {Roo.bootstrap.LocationPicker} this
29528          * @param {Map event} e
29529          */
29530         markerRightClick : true,
29531         /**
29532          * @event OverlayViewDraw
29533          * Fires when OverlayView Draw
29534          * @param {Roo.bootstrap.LocationPicker} this
29535          */
29536         OverlayViewDraw : true,
29537         /**
29538          * @event OverlayViewOnAdd
29539          * Fires when OverlayView Draw
29540          * @param {Roo.bootstrap.LocationPicker} this
29541          */
29542         OverlayViewOnAdd : true,
29543         /**
29544          * @event OverlayViewOnRemove
29545          * Fires when OverlayView Draw
29546          * @param {Roo.bootstrap.LocationPicker} this
29547          */
29548         OverlayViewOnRemove : true,
29549         /**
29550          * @event OverlayViewShow
29551          * Fires when OverlayView Draw
29552          * @param {Roo.bootstrap.LocationPicker} this
29553          * @param {Pixel} cpx
29554          */
29555         OverlayViewShow : true,
29556         /**
29557          * @event OverlayViewHide
29558          * Fires when OverlayView Draw
29559          * @param {Roo.bootstrap.LocationPicker} this
29560          */
29561         OverlayViewHide : true,
29562         /**
29563          * @event loadexception
29564          * Fires when load google lib failed.
29565          * @param {Roo.bootstrap.LocationPicker} this
29566          */
29567         loadexception : true
29568     });
29569         
29570 };
29571
29572 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29573     
29574     gMapContext: false,
29575     
29576     latitude: 0,
29577     longitude: 0,
29578     zoom: 15,
29579     mapTypeId: false,
29580     mapTypeControl: false,
29581     disableDoubleClickZoom: false,
29582     scrollwheel: true,
29583     streetViewControl: false,
29584     radius: 0,
29585     locationName: '',
29586     draggable: true,
29587     enableAutocomplete: false,
29588     enableReverseGeocode: true,
29589     markerTitle: '',
29590     
29591     getAutoCreate: function()
29592     {
29593
29594         var cfg = {
29595             tag: 'div',
29596             cls: 'roo-location-picker'
29597         };
29598         
29599         return cfg
29600     },
29601     
29602     initEvents: function(ct, position)
29603     {       
29604         if(!this.el.getWidth() || this.isApplied()){
29605             return;
29606         }
29607         
29608         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29609         
29610         this.initial();
29611     },
29612     
29613     initial: function()
29614     {
29615         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29616             this.fireEvent('loadexception', this);
29617             return;
29618         }
29619         
29620         if(!this.mapTypeId){
29621             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29622         }
29623         
29624         this.gMapContext = this.GMapContext();
29625         
29626         this.initOverlayView();
29627         
29628         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29629         
29630         var _this = this;
29631                 
29632         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29633             _this.setPosition(_this.gMapContext.marker.position);
29634         });
29635         
29636         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29637             _this.fireEvent('mapClick', this, event);
29638             
29639         });
29640
29641         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29642             _this.fireEvent('mapRightClick', this, event);
29643             
29644         });
29645         
29646         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29647             _this.fireEvent('markerClick', this, event);
29648             
29649         });
29650
29651         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29652             _this.fireEvent('markerRightClick', this, event);
29653             
29654         });
29655         
29656         this.setPosition(this.gMapContext.location);
29657         
29658         this.fireEvent('initial', this, this.gMapContext.location);
29659     },
29660     
29661     initOverlayView: function()
29662     {
29663         var _this = this;
29664         
29665         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29666             
29667             draw: function()
29668             {
29669                 _this.fireEvent('OverlayViewDraw', _this);
29670             },
29671             
29672             onAdd: function()
29673             {
29674                 _this.fireEvent('OverlayViewOnAdd', _this);
29675             },
29676             
29677             onRemove: function()
29678             {
29679                 _this.fireEvent('OverlayViewOnRemove', _this);
29680             },
29681             
29682             show: function(cpx)
29683             {
29684                 _this.fireEvent('OverlayViewShow', _this, cpx);
29685             },
29686             
29687             hide: function()
29688             {
29689                 _this.fireEvent('OverlayViewHide', _this);
29690             }
29691             
29692         });
29693     },
29694     
29695     fromLatLngToContainerPixel: function(event)
29696     {
29697         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29698     },
29699     
29700     isApplied: function() 
29701     {
29702         return this.getGmapContext() == false ? false : true;
29703     },
29704     
29705     getGmapContext: function() 
29706     {
29707         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29708     },
29709     
29710     GMapContext: function() 
29711     {
29712         var position = new google.maps.LatLng(this.latitude, this.longitude);
29713         
29714         var _map = new google.maps.Map(this.el.dom, {
29715             center: position,
29716             zoom: this.zoom,
29717             mapTypeId: this.mapTypeId,
29718             mapTypeControl: this.mapTypeControl,
29719             disableDoubleClickZoom: this.disableDoubleClickZoom,
29720             scrollwheel: this.scrollwheel,
29721             streetViewControl: this.streetViewControl,
29722             locationName: this.locationName,
29723             draggable: this.draggable,
29724             enableAutocomplete: this.enableAutocomplete,
29725             enableReverseGeocode: this.enableReverseGeocode
29726         });
29727         
29728         var _marker = new google.maps.Marker({
29729             position: position,
29730             map: _map,
29731             title: this.markerTitle,
29732             draggable: this.draggable
29733         });
29734         
29735         return {
29736             map: _map,
29737             marker: _marker,
29738             circle: null,
29739             location: position,
29740             radius: this.radius,
29741             locationName: this.locationName,
29742             addressComponents: {
29743                 formatted_address: null,
29744                 addressLine1: null,
29745                 addressLine2: null,
29746                 streetName: null,
29747                 streetNumber: null,
29748                 city: null,
29749                 district: null,
29750                 state: null,
29751                 stateOrProvince: null
29752             },
29753             settings: this,
29754             domContainer: this.el.dom,
29755             geodecoder: new google.maps.Geocoder()
29756         };
29757     },
29758     
29759     drawCircle: function(center, radius, options) 
29760     {
29761         if (this.gMapContext.circle != null) {
29762             this.gMapContext.circle.setMap(null);
29763         }
29764         if (radius > 0) {
29765             radius *= 1;
29766             options = Roo.apply({}, options, {
29767                 strokeColor: "#0000FF",
29768                 strokeOpacity: .35,
29769                 strokeWeight: 2,
29770                 fillColor: "#0000FF",
29771                 fillOpacity: .2
29772             });
29773             
29774             options.map = this.gMapContext.map;
29775             options.radius = radius;
29776             options.center = center;
29777             this.gMapContext.circle = new google.maps.Circle(options);
29778             return this.gMapContext.circle;
29779         }
29780         
29781         return null;
29782     },
29783     
29784     setPosition: function(location) 
29785     {
29786         this.gMapContext.location = location;
29787         this.gMapContext.marker.setPosition(location);
29788         this.gMapContext.map.panTo(location);
29789         this.drawCircle(location, this.gMapContext.radius, {});
29790         
29791         var _this = this;
29792         
29793         if (this.gMapContext.settings.enableReverseGeocode) {
29794             this.gMapContext.geodecoder.geocode({
29795                 latLng: this.gMapContext.location
29796             }, function(results, status) {
29797                 
29798                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29799                     _this.gMapContext.locationName = results[0].formatted_address;
29800                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29801                     
29802                     _this.fireEvent('positionchanged', this, location);
29803                 }
29804             });
29805             
29806             return;
29807         }
29808         
29809         this.fireEvent('positionchanged', this, location);
29810     },
29811     
29812     resize: function()
29813     {
29814         google.maps.event.trigger(this.gMapContext.map, "resize");
29815         
29816         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29817         
29818         this.fireEvent('resize', this);
29819     },
29820     
29821     setPositionByLatLng: function(latitude, longitude)
29822     {
29823         this.setPosition(new google.maps.LatLng(latitude, longitude));
29824     },
29825     
29826     getCurrentPosition: function() 
29827     {
29828         return {
29829             latitude: this.gMapContext.location.lat(),
29830             longitude: this.gMapContext.location.lng()
29831         };
29832     },
29833     
29834     getAddressName: function() 
29835     {
29836         return this.gMapContext.locationName;
29837     },
29838     
29839     getAddressComponents: function() 
29840     {
29841         return this.gMapContext.addressComponents;
29842     },
29843     
29844     address_component_from_google_geocode: function(address_components) 
29845     {
29846         var result = {};
29847         
29848         for (var i = 0; i < address_components.length; i++) {
29849             var component = address_components[i];
29850             if (component.types.indexOf("postal_code") >= 0) {
29851                 result.postalCode = component.short_name;
29852             } else if (component.types.indexOf("street_number") >= 0) {
29853                 result.streetNumber = component.short_name;
29854             } else if (component.types.indexOf("route") >= 0) {
29855                 result.streetName = component.short_name;
29856             } else if (component.types.indexOf("neighborhood") >= 0) {
29857                 result.city = component.short_name;
29858             } else if (component.types.indexOf("locality") >= 0) {
29859                 result.city = component.short_name;
29860             } else if (component.types.indexOf("sublocality") >= 0) {
29861                 result.district = component.short_name;
29862             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29863                 result.stateOrProvince = component.short_name;
29864             } else if (component.types.indexOf("country") >= 0) {
29865                 result.country = component.short_name;
29866             }
29867         }
29868         
29869         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29870         result.addressLine2 = "";
29871         return result;
29872     },
29873     
29874     setZoomLevel: function(zoom)
29875     {
29876         this.gMapContext.map.setZoom(zoom);
29877     },
29878     
29879     show: function()
29880     {
29881         if(!this.el){
29882             return;
29883         }
29884         
29885         this.el.show();
29886         
29887         this.resize();
29888         
29889         this.fireEvent('show', this);
29890     },
29891     
29892     hide: function()
29893     {
29894         if(!this.el){
29895             return;
29896         }
29897         
29898         this.el.hide();
29899         
29900         this.fireEvent('hide', this);
29901     }
29902     
29903 });
29904
29905 Roo.apply(Roo.bootstrap.LocationPicker, {
29906     
29907     OverlayView : function(map, options)
29908     {
29909         options = options || {};
29910         
29911         this.setMap(map);
29912     }
29913     
29914     
29915 });/**
29916  * @class Roo.bootstrap.Alert
29917  * @extends Roo.bootstrap.Component
29918  * Bootstrap Alert class - shows an alert area box
29919  * eg
29920  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29921   Enter a valid email address
29922 </div>
29923  * @licence LGPL
29924  * @cfg {String} title The title of alert
29925  * @cfg {String} html The content of alert
29926  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29927  * @cfg {String} fa font-awesomeicon
29928  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29929  * @cfg {Boolean} close true to show a x closer
29930  * 
29931  * 
29932  * @constructor
29933  * Create a new alert
29934  * @param {Object} config The config object
29935  */
29936
29937
29938 Roo.bootstrap.Alert = function(config){
29939     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29940     
29941 };
29942
29943 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29944     
29945     title: '',
29946     html: '',
29947     weight: false,
29948     fa: false,
29949     faicon: false, // BC
29950     close : false,
29951     
29952     
29953     getAutoCreate : function()
29954     {
29955         
29956         var cfg = {
29957             tag : 'div',
29958             cls : 'alert',
29959             cn : [
29960                 {
29961                     tag: 'button',
29962                     type :  "button",
29963                     cls: "close",
29964                     html : '×',
29965                     style : this.close ? '' : 'display:none'
29966                 },
29967                 {
29968                     tag : 'i',
29969                     cls : 'roo-alert-icon'
29970                     
29971                 },
29972                 {
29973                     tag : 'b',
29974                     cls : 'roo-alert-title',
29975                     html : this.title
29976                 },
29977                 {
29978                     tag : 'span',
29979                     cls : 'roo-alert-text',
29980                     html : this.html
29981                 }
29982             ]
29983         };
29984         
29985         if(this.faicon){
29986             cfg.cn[0].cls += ' fa ' + this.faicon;
29987         }
29988         if(this.fa){
29989             cfg.cn[0].cls += ' fa ' + this.fa;
29990         }
29991         
29992         if(this.weight){
29993             cfg.cls += ' alert-' + this.weight;
29994         }
29995         
29996         return cfg;
29997     },
29998     
29999     initEvents: function() 
30000     {
30001         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30002         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30003         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30004         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30005         if (this.seconds > 0) {
30006             this.hide.defer(this.seconds, this);
30007         }
30008     },
30009     /**
30010      * Set the Title Message HTML
30011      * @param {String} html
30012      */
30013     setTitle : function(str)
30014     {
30015         this.titleEl.dom.innerHTML = str;
30016     },
30017      
30018      /**
30019      * Set the Body Message HTML
30020      * @param {String} html
30021      */
30022     setHtml : function(str)
30023     {
30024         this.htmlEl.dom.innerHTML = str;
30025     },
30026     /**
30027      * Set the Weight of the alert
30028      * @param {String} (success|info|warning|danger) weight
30029      */
30030     
30031     setWeight : function(weight)
30032     {
30033         if(this.weight){
30034             this.el.removeClass('alert-' + this.weight);
30035         }
30036         
30037         this.weight = weight;
30038         
30039         this.el.addClass('alert-' + this.weight);
30040     },
30041       /**
30042      * Set the Icon of the alert
30043      * @param {String} see fontawsome names (name without the 'fa-' bit)
30044      */
30045     setIcon : function(icon)
30046     {
30047         if(this.faicon){
30048             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30049         }
30050         
30051         this.faicon = icon;
30052         
30053         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30054     },
30055     /**
30056      * Hide the Alert
30057      */
30058     hide: function() 
30059     {
30060         this.el.hide();   
30061     },
30062     /**
30063      * Show the Alert
30064      */
30065     show: function() 
30066     {  
30067         this.el.show();   
30068     }
30069     
30070 });
30071
30072  
30073 /*
30074 * Licence: LGPL
30075 */
30076
30077 /**
30078  * @class Roo.bootstrap.UploadCropbox
30079  * @extends Roo.bootstrap.Component
30080  * Bootstrap UploadCropbox class
30081  * @cfg {String} emptyText show when image has been loaded
30082  * @cfg {String} rotateNotify show when image too small to rotate
30083  * @cfg {Number} errorTimeout default 3000
30084  * @cfg {Number} minWidth default 300
30085  * @cfg {Number} minHeight default 300
30086  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30087  * @cfg {Boolean} isDocument (true|false) default false
30088  * @cfg {String} url action url
30089  * @cfg {String} paramName default 'imageUpload'
30090  * @cfg {String} method default POST
30091  * @cfg {Boolean} loadMask (true|false) default true
30092  * @cfg {Boolean} loadingText default 'Loading...'
30093  * 
30094  * @constructor
30095  * Create a new UploadCropbox
30096  * @param {Object} config The config object
30097  */
30098
30099 Roo.bootstrap.UploadCropbox = function(config){
30100     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30101     
30102     this.addEvents({
30103         /**
30104          * @event beforeselectfile
30105          * Fire before select file
30106          * @param {Roo.bootstrap.UploadCropbox} this
30107          */
30108         "beforeselectfile" : true,
30109         /**
30110          * @event initial
30111          * Fire after initEvent
30112          * @param {Roo.bootstrap.UploadCropbox} this
30113          */
30114         "initial" : true,
30115         /**
30116          * @event crop
30117          * Fire after initEvent
30118          * @param {Roo.bootstrap.UploadCropbox} this
30119          * @param {String} data
30120          */
30121         "crop" : true,
30122         /**
30123          * @event prepare
30124          * Fire when preparing the file data
30125          * @param {Roo.bootstrap.UploadCropbox} this
30126          * @param {Object} file
30127          */
30128         "prepare" : true,
30129         /**
30130          * @event exception
30131          * Fire when get exception
30132          * @param {Roo.bootstrap.UploadCropbox} this
30133          * @param {XMLHttpRequest} xhr
30134          */
30135         "exception" : true,
30136         /**
30137          * @event beforeloadcanvas
30138          * Fire before load the canvas
30139          * @param {Roo.bootstrap.UploadCropbox} this
30140          * @param {String} src
30141          */
30142         "beforeloadcanvas" : true,
30143         /**
30144          * @event trash
30145          * Fire when trash image
30146          * @param {Roo.bootstrap.UploadCropbox} this
30147          */
30148         "trash" : true,
30149         /**
30150          * @event download
30151          * Fire when download the image
30152          * @param {Roo.bootstrap.UploadCropbox} this
30153          */
30154         "download" : true,
30155         /**
30156          * @event footerbuttonclick
30157          * Fire when footerbuttonclick
30158          * @param {Roo.bootstrap.UploadCropbox} this
30159          * @param {String} type
30160          */
30161         "footerbuttonclick" : true,
30162         /**
30163          * @event resize
30164          * Fire when resize
30165          * @param {Roo.bootstrap.UploadCropbox} this
30166          */
30167         "resize" : true,
30168         /**
30169          * @event rotate
30170          * Fire when rotate the image
30171          * @param {Roo.bootstrap.UploadCropbox} this
30172          * @param {String} pos
30173          */
30174         "rotate" : true,
30175         /**
30176          * @event inspect
30177          * Fire when inspect the file
30178          * @param {Roo.bootstrap.UploadCropbox} this
30179          * @param {Object} file
30180          */
30181         "inspect" : true,
30182         /**
30183          * @event upload
30184          * Fire when xhr upload the file
30185          * @param {Roo.bootstrap.UploadCropbox} this
30186          * @param {Object} data
30187          */
30188         "upload" : true,
30189         /**
30190          * @event arrange
30191          * Fire when arrange the file data
30192          * @param {Roo.bootstrap.UploadCropbox} this
30193          * @param {Object} formData
30194          */
30195         "arrange" : true
30196     });
30197     
30198     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30199 };
30200
30201 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30202     
30203     emptyText : 'Click to upload image',
30204     rotateNotify : 'Image is too small to rotate',
30205     errorTimeout : 3000,
30206     scale : 0,
30207     baseScale : 1,
30208     rotate : 0,
30209     dragable : false,
30210     pinching : false,
30211     mouseX : 0,
30212     mouseY : 0,
30213     cropData : false,
30214     minWidth : 300,
30215     minHeight : 300,
30216     file : false,
30217     exif : {},
30218     baseRotate : 1,
30219     cropType : 'image/jpeg',
30220     buttons : false,
30221     canvasLoaded : false,
30222     isDocument : false,
30223     method : 'POST',
30224     paramName : 'imageUpload',
30225     loadMask : true,
30226     loadingText : 'Loading...',
30227     maskEl : false,
30228     
30229     getAutoCreate : function()
30230     {
30231         var cfg = {
30232             tag : 'div',
30233             cls : 'roo-upload-cropbox',
30234             cn : [
30235                 {
30236                     tag : 'input',
30237                     cls : 'roo-upload-cropbox-selector',
30238                     type : 'file'
30239                 },
30240                 {
30241                     tag : 'div',
30242                     cls : 'roo-upload-cropbox-body',
30243                     style : 'cursor:pointer',
30244                     cn : [
30245                         {
30246                             tag : 'div',
30247                             cls : 'roo-upload-cropbox-preview'
30248                         },
30249                         {
30250                             tag : 'div',
30251                             cls : 'roo-upload-cropbox-thumb'
30252                         },
30253                         {
30254                             tag : 'div',
30255                             cls : 'roo-upload-cropbox-empty-notify',
30256                             html : this.emptyText
30257                         },
30258                         {
30259                             tag : 'div',
30260                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30261                             html : this.rotateNotify
30262                         }
30263                     ]
30264                 },
30265                 {
30266                     tag : 'div',
30267                     cls : 'roo-upload-cropbox-footer',
30268                     cn : {
30269                         tag : 'div',
30270                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30271                         cn : []
30272                     }
30273                 }
30274             ]
30275         };
30276         
30277         return cfg;
30278     },
30279     
30280     onRender : function(ct, position)
30281     {
30282         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30283         
30284         if (this.buttons.length) {
30285             
30286             Roo.each(this.buttons, function(bb) {
30287                 
30288                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30289                 
30290                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30291                 
30292             }, this);
30293         }
30294         
30295         if(this.loadMask){
30296             this.maskEl = this.el;
30297         }
30298     },
30299     
30300     initEvents : function()
30301     {
30302         this.urlAPI = (window.createObjectURL && window) || 
30303                                 (window.URL && URL.revokeObjectURL && URL) || 
30304                                 (window.webkitURL && webkitURL);
30305                         
30306         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30307         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30308         
30309         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30310         this.selectorEl.hide();
30311         
30312         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30313         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30314         
30315         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30316         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30317         this.thumbEl.hide();
30318         
30319         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30320         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30321         
30322         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30323         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30324         this.errorEl.hide();
30325         
30326         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30327         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30328         this.footerEl.hide();
30329         
30330         this.setThumbBoxSize();
30331         
30332         this.bind();
30333         
30334         this.resize();
30335         
30336         this.fireEvent('initial', this);
30337     },
30338
30339     bind : function()
30340     {
30341         var _this = this;
30342         
30343         window.addEventListener("resize", function() { _this.resize(); } );
30344         
30345         this.bodyEl.on('click', this.beforeSelectFile, this);
30346         
30347         if(Roo.isTouch){
30348             this.bodyEl.on('touchstart', this.onTouchStart, this);
30349             this.bodyEl.on('touchmove', this.onTouchMove, this);
30350             this.bodyEl.on('touchend', this.onTouchEnd, this);
30351         }
30352         
30353         if(!Roo.isTouch){
30354             this.bodyEl.on('mousedown', this.onMouseDown, this);
30355             this.bodyEl.on('mousemove', this.onMouseMove, this);
30356             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30357             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30358             Roo.get(document).on('mouseup', this.onMouseUp, this);
30359         }
30360         
30361         this.selectorEl.on('change', this.onFileSelected, this);
30362     },
30363     
30364     reset : function()
30365     {    
30366         this.scale = 0;
30367         this.baseScale = 1;
30368         this.rotate = 0;
30369         this.baseRotate = 1;
30370         this.dragable = false;
30371         this.pinching = false;
30372         this.mouseX = 0;
30373         this.mouseY = 0;
30374         this.cropData = false;
30375         this.notifyEl.dom.innerHTML = this.emptyText;
30376         
30377         this.selectorEl.dom.value = '';
30378         
30379     },
30380     
30381     resize : function()
30382     {
30383         if(this.fireEvent('resize', this) != false){
30384             this.setThumbBoxPosition();
30385             this.setCanvasPosition();
30386         }
30387     },
30388     
30389     onFooterButtonClick : function(e, el, o, type)
30390     {
30391         switch (type) {
30392             case 'rotate-left' :
30393                 this.onRotateLeft(e);
30394                 break;
30395             case 'rotate-right' :
30396                 this.onRotateRight(e);
30397                 break;
30398             case 'picture' :
30399                 this.beforeSelectFile(e);
30400                 break;
30401             case 'trash' :
30402                 this.trash(e);
30403                 break;
30404             case 'crop' :
30405                 this.crop(e);
30406                 break;
30407             case 'download' :
30408                 this.download(e);
30409                 break;
30410             default :
30411                 break;
30412         }
30413         
30414         this.fireEvent('footerbuttonclick', this, type);
30415     },
30416     
30417     beforeSelectFile : function(e)
30418     {
30419         e.preventDefault();
30420         
30421         if(this.fireEvent('beforeselectfile', this) != false){
30422             this.selectorEl.dom.click();
30423         }
30424     },
30425     
30426     onFileSelected : function(e)
30427     {
30428         e.preventDefault();
30429         
30430         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30431             return;
30432         }
30433         
30434         var file = this.selectorEl.dom.files[0];
30435         
30436         if(this.fireEvent('inspect', this, file) != false){
30437             this.prepare(file);
30438         }
30439         
30440     },
30441     
30442     trash : function(e)
30443     {
30444         this.fireEvent('trash', this);
30445     },
30446     
30447     download : function(e)
30448     {
30449         this.fireEvent('download', this);
30450     },
30451     
30452     loadCanvas : function(src)
30453     {   
30454         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30455             
30456             this.reset();
30457             
30458             this.imageEl = document.createElement('img');
30459             
30460             var _this = this;
30461             
30462             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30463             
30464             this.imageEl.src = src;
30465         }
30466     },
30467     
30468     onLoadCanvas : function()
30469     {   
30470         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30471         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30472         
30473         this.bodyEl.un('click', this.beforeSelectFile, this);
30474         
30475         this.notifyEl.hide();
30476         this.thumbEl.show();
30477         this.footerEl.show();
30478         
30479         this.baseRotateLevel();
30480         
30481         if(this.isDocument){
30482             this.setThumbBoxSize();
30483         }
30484         
30485         this.setThumbBoxPosition();
30486         
30487         this.baseScaleLevel();
30488         
30489         this.draw();
30490         
30491         this.resize();
30492         
30493         this.canvasLoaded = true;
30494         
30495         if(this.loadMask){
30496             this.maskEl.unmask();
30497         }
30498         
30499     },
30500     
30501     setCanvasPosition : function()
30502     {   
30503         if(!this.canvasEl){
30504             return;
30505         }
30506         
30507         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30508         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30509         
30510         this.previewEl.setLeft(pw);
30511         this.previewEl.setTop(ph);
30512         
30513     },
30514     
30515     onMouseDown : function(e)
30516     {   
30517         e.stopEvent();
30518         
30519         this.dragable = true;
30520         this.pinching = false;
30521         
30522         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30523             this.dragable = false;
30524             return;
30525         }
30526         
30527         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30528         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30529         
30530     },
30531     
30532     onMouseMove : function(e)
30533     {   
30534         e.stopEvent();
30535         
30536         if(!this.canvasLoaded){
30537             return;
30538         }
30539         
30540         if (!this.dragable){
30541             return;
30542         }
30543         
30544         var minX = Math.ceil(this.thumbEl.getLeft(true));
30545         var minY = Math.ceil(this.thumbEl.getTop(true));
30546         
30547         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30548         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30549         
30550         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30551         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30552         
30553         x = x - this.mouseX;
30554         y = y - this.mouseY;
30555         
30556         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30557         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30558         
30559         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30560         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30561         
30562         this.previewEl.setLeft(bgX);
30563         this.previewEl.setTop(bgY);
30564         
30565         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30566         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30567     },
30568     
30569     onMouseUp : function(e)
30570     {   
30571         e.stopEvent();
30572         
30573         this.dragable = false;
30574     },
30575     
30576     onMouseWheel : function(e)
30577     {   
30578         e.stopEvent();
30579         
30580         this.startScale = this.scale;
30581         
30582         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30583         
30584         if(!this.zoomable()){
30585             this.scale = this.startScale;
30586             return;
30587         }
30588         
30589         this.draw();
30590         
30591         return;
30592     },
30593     
30594     zoomable : function()
30595     {
30596         var minScale = this.thumbEl.getWidth() / this.minWidth;
30597         
30598         if(this.minWidth < this.minHeight){
30599             minScale = this.thumbEl.getHeight() / this.minHeight;
30600         }
30601         
30602         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30603         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30604         
30605         if(
30606                 this.isDocument &&
30607                 (this.rotate == 0 || this.rotate == 180) && 
30608                 (
30609                     width > this.imageEl.OriginWidth || 
30610                     height > this.imageEl.OriginHeight ||
30611                     (width < this.minWidth && height < this.minHeight)
30612                 )
30613         ){
30614             return false;
30615         }
30616         
30617         if(
30618                 this.isDocument &&
30619                 (this.rotate == 90 || this.rotate == 270) && 
30620                 (
30621                     width > this.imageEl.OriginWidth || 
30622                     height > this.imageEl.OriginHeight ||
30623                     (width < this.minHeight && height < this.minWidth)
30624                 )
30625         ){
30626             return false;
30627         }
30628         
30629         if(
30630                 !this.isDocument &&
30631                 (this.rotate == 0 || this.rotate == 180) && 
30632                 (
30633                     width < this.minWidth || 
30634                     width > this.imageEl.OriginWidth || 
30635                     height < this.minHeight || 
30636                     height > this.imageEl.OriginHeight
30637                 )
30638         ){
30639             return false;
30640         }
30641         
30642         if(
30643                 !this.isDocument &&
30644                 (this.rotate == 90 || this.rotate == 270) && 
30645                 (
30646                     width < this.minHeight || 
30647                     width > this.imageEl.OriginWidth || 
30648                     height < this.minWidth || 
30649                     height > this.imageEl.OriginHeight
30650                 )
30651         ){
30652             return false;
30653         }
30654         
30655         return true;
30656         
30657     },
30658     
30659     onRotateLeft : function(e)
30660     {   
30661         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30662             
30663             var minScale = this.thumbEl.getWidth() / this.minWidth;
30664             
30665             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30666             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30667             
30668             this.startScale = this.scale;
30669             
30670             while (this.getScaleLevel() < minScale){
30671             
30672                 this.scale = this.scale + 1;
30673                 
30674                 if(!this.zoomable()){
30675                     break;
30676                 }
30677                 
30678                 if(
30679                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30680                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30681                 ){
30682                     continue;
30683                 }
30684                 
30685                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30686
30687                 this.draw();
30688                 
30689                 return;
30690             }
30691             
30692             this.scale = this.startScale;
30693             
30694             this.onRotateFail();
30695             
30696             return false;
30697         }
30698         
30699         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30700
30701         if(this.isDocument){
30702             this.setThumbBoxSize();
30703             this.setThumbBoxPosition();
30704             this.setCanvasPosition();
30705         }
30706         
30707         this.draw();
30708         
30709         this.fireEvent('rotate', this, 'left');
30710         
30711     },
30712     
30713     onRotateRight : function(e)
30714     {
30715         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30716             
30717             var minScale = this.thumbEl.getWidth() / this.minWidth;
30718         
30719             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30720             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30721             
30722             this.startScale = this.scale;
30723             
30724             while (this.getScaleLevel() < minScale){
30725             
30726                 this.scale = this.scale + 1;
30727                 
30728                 if(!this.zoomable()){
30729                     break;
30730                 }
30731                 
30732                 if(
30733                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30734                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30735                 ){
30736                     continue;
30737                 }
30738                 
30739                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30740
30741                 this.draw();
30742                 
30743                 return;
30744             }
30745             
30746             this.scale = this.startScale;
30747             
30748             this.onRotateFail();
30749             
30750             return false;
30751         }
30752         
30753         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30754
30755         if(this.isDocument){
30756             this.setThumbBoxSize();
30757             this.setThumbBoxPosition();
30758             this.setCanvasPosition();
30759         }
30760         
30761         this.draw();
30762         
30763         this.fireEvent('rotate', this, 'right');
30764     },
30765     
30766     onRotateFail : function()
30767     {
30768         this.errorEl.show(true);
30769         
30770         var _this = this;
30771         
30772         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30773     },
30774     
30775     draw : function()
30776     {
30777         this.previewEl.dom.innerHTML = '';
30778         
30779         var canvasEl = document.createElement("canvas");
30780         
30781         var contextEl = canvasEl.getContext("2d");
30782         
30783         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30784         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30785         var center = this.imageEl.OriginWidth / 2;
30786         
30787         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30788             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30789             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30790             center = this.imageEl.OriginHeight / 2;
30791         }
30792         
30793         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30794         
30795         contextEl.translate(center, center);
30796         contextEl.rotate(this.rotate * Math.PI / 180);
30797
30798         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30799         
30800         this.canvasEl = document.createElement("canvas");
30801         
30802         this.contextEl = this.canvasEl.getContext("2d");
30803         
30804         switch (this.rotate) {
30805             case 0 :
30806                 
30807                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30808                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30809                 
30810                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30811                 
30812                 break;
30813             case 90 : 
30814                 
30815                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30816                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30817                 
30818                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30819                     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);
30820                     break;
30821                 }
30822                 
30823                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30824                 
30825                 break;
30826             case 180 :
30827                 
30828                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30829                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30830                 
30831                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30832                     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);
30833                     break;
30834                 }
30835                 
30836                 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);
30837                 
30838                 break;
30839             case 270 :
30840                 
30841                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30842                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30843         
30844                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30845                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30846                     break;
30847                 }
30848                 
30849                 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);
30850                 
30851                 break;
30852             default : 
30853                 break;
30854         }
30855         
30856         this.previewEl.appendChild(this.canvasEl);
30857         
30858         this.setCanvasPosition();
30859     },
30860     
30861     crop : function()
30862     {
30863         if(!this.canvasLoaded){
30864             return;
30865         }
30866         
30867         var imageCanvas = document.createElement("canvas");
30868         
30869         var imageContext = imageCanvas.getContext("2d");
30870         
30871         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30872         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30873         
30874         var center = imageCanvas.width / 2;
30875         
30876         imageContext.translate(center, center);
30877         
30878         imageContext.rotate(this.rotate * Math.PI / 180);
30879         
30880         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30881         
30882         var canvas = document.createElement("canvas");
30883         
30884         var context = canvas.getContext("2d");
30885                 
30886         canvas.width = this.minWidth;
30887         canvas.height = this.minHeight;
30888
30889         switch (this.rotate) {
30890             case 0 :
30891                 
30892                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30893                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30894                 
30895                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30896                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30897                 
30898                 var targetWidth = this.minWidth - 2 * x;
30899                 var targetHeight = this.minHeight - 2 * y;
30900                 
30901                 var scale = 1;
30902                 
30903                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30904                     scale = targetWidth / width;
30905                 }
30906                 
30907                 if(x > 0 && y == 0){
30908                     scale = targetHeight / height;
30909                 }
30910                 
30911                 if(x > 0 && y > 0){
30912                     scale = targetWidth / width;
30913                     
30914                     if(width < height){
30915                         scale = targetHeight / height;
30916                     }
30917                 }
30918                 
30919                 context.scale(scale, scale);
30920                 
30921                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30922                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30923
30924                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30925                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30926
30927                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30928                 
30929                 break;
30930             case 90 : 
30931                 
30932                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30933                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30934                 
30935                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30936                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30937                 
30938                 var targetWidth = this.minWidth - 2 * x;
30939                 var targetHeight = this.minHeight - 2 * y;
30940                 
30941                 var scale = 1;
30942                 
30943                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30944                     scale = targetWidth / width;
30945                 }
30946                 
30947                 if(x > 0 && y == 0){
30948                     scale = targetHeight / height;
30949                 }
30950                 
30951                 if(x > 0 && y > 0){
30952                     scale = targetWidth / width;
30953                     
30954                     if(width < height){
30955                         scale = targetHeight / height;
30956                     }
30957                 }
30958                 
30959                 context.scale(scale, scale);
30960                 
30961                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30962                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30963
30964                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30965                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30966                 
30967                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30968                 
30969                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30970                 
30971                 break;
30972             case 180 :
30973                 
30974                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30975                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30976                 
30977                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30978                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30979                 
30980                 var targetWidth = this.minWidth - 2 * x;
30981                 var targetHeight = this.minHeight - 2 * y;
30982                 
30983                 var scale = 1;
30984                 
30985                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30986                     scale = targetWidth / width;
30987                 }
30988                 
30989                 if(x > 0 && y == 0){
30990                     scale = targetHeight / height;
30991                 }
30992                 
30993                 if(x > 0 && y > 0){
30994                     scale = targetWidth / width;
30995                     
30996                     if(width < height){
30997                         scale = targetHeight / height;
30998                     }
30999                 }
31000                 
31001                 context.scale(scale, scale);
31002                 
31003                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31004                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31005
31006                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31007                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31008
31009                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31010                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31011                 
31012                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31013                 
31014                 break;
31015             case 270 :
31016                 
31017                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31018                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31019                 
31020                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31021                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31022                 
31023                 var targetWidth = this.minWidth - 2 * x;
31024                 var targetHeight = this.minHeight - 2 * y;
31025                 
31026                 var scale = 1;
31027                 
31028                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31029                     scale = targetWidth / width;
31030                 }
31031                 
31032                 if(x > 0 && y == 0){
31033                     scale = targetHeight / height;
31034                 }
31035                 
31036                 if(x > 0 && y > 0){
31037                     scale = targetWidth / width;
31038                     
31039                     if(width < height){
31040                         scale = targetHeight / height;
31041                     }
31042                 }
31043                 
31044                 context.scale(scale, scale);
31045                 
31046                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31047                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31048
31049                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31050                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31051                 
31052                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31053                 
31054                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31055                 
31056                 break;
31057             default : 
31058                 break;
31059         }
31060         
31061         this.cropData = canvas.toDataURL(this.cropType);
31062         
31063         if(this.fireEvent('crop', this, this.cropData) !== false){
31064             this.process(this.file, this.cropData);
31065         }
31066         
31067         return;
31068         
31069     },
31070     
31071     setThumbBoxSize : function()
31072     {
31073         var width, height;
31074         
31075         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31076             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31077             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31078             
31079             this.minWidth = width;
31080             this.minHeight = height;
31081             
31082             if(this.rotate == 90 || this.rotate == 270){
31083                 this.minWidth = height;
31084                 this.minHeight = width;
31085             }
31086         }
31087         
31088         height = 300;
31089         width = Math.ceil(this.minWidth * height / this.minHeight);
31090         
31091         if(this.minWidth > this.minHeight){
31092             width = 300;
31093             height = Math.ceil(this.minHeight * width / this.minWidth);
31094         }
31095         
31096         this.thumbEl.setStyle({
31097             width : width + 'px',
31098             height : height + 'px'
31099         });
31100
31101         return;
31102             
31103     },
31104     
31105     setThumbBoxPosition : function()
31106     {
31107         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31108         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31109         
31110         this.thumbEl.setLeft(x);
31111         this.thumbEl.setTop(y);
31112         
31113     },
31114     
31115     baseRotateLevel : function()
31116     {
31117         this.baseRotate = 1;
31118         
31119         if(
31120                 typeof(this.exif) != 'undefined' &&
31121                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31122                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31123         ){
31124             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31125         }
31126         
31127         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31128         
31129     },
31130     
31131     baseScaleLevel : function()
31132     {
31133         var width, height;
31134         
31135         if(this.isDocument){
31136             
31137             if(this.baseRotate == 6 || this.baseRotate == 8){
31138             
31139                 height = this.thumbEl.getHeight();
31140                 this.baseScale = height / this.imageEl.OriginWidth;
31141
31142                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31143                     width = this.thumbEl.getWidth();
31144                     this.baseScale = width / this.imageEl.OriginHeight;
31145                 }
31146
31147                 return;
31148             }
31149
31150             height = this.thumbEl.getHeight();
31151             this.baseScale = height / this.imageEl.OriginHeight;
31152
31153             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31154                 width = this.thumbEl.getWidth();
31155                 this.baseScale = width / this.imageEl.OriginWidth;
31156             }
31157
31158             return;
31159         }
31160         
31161         if(this.baseRotate == 6 || this.baseRotate == 8){
31162             
31163             width = this.thumbEl.getHeight();
31164             this.baseScale = width / this.imageEl.OriginHeight;
31165             
31166             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31167                 height = this.thumbEl.getWidth();
31168                 this.baseScale = height / this.imageEl.OriginHeight;
31169             }
31170             
31171             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31172                 height = this.thumbEl.getWidth();
31173                 this.baseScale = height / this.imageEl.OriginHeight;
31174                 
31175                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31176                     width = this.thumbEl.getHeight();
31177                     this.baseScale = width / this.imageEl.OriginWidth;
31178                 }
31179             }
31180             
31181             return;
31182         }
31183         
31184         width = this.thumbEl.getWidth();
31185         this.baseScale = width / this.imageEl.OriginWidth;
31186         
31187         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31188             height = this.thumbEl.getHeight();
31189             this.baseScale = height / this.imageEl.OriginHeight;
31190         }
31191         
31192         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31193             
31194             height = this.thumbEl.getHeight();
31195             this.baseScale = height / this.imageEl.OriginHeight;
31196             
31197             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31198                 width = this.thumbEl.getWidth();
31199                 this.baseScale = width / this.imageEl.OriginWidth;
31200             }
31201             
31202         }
31203         
31204         return;
31205     },
31206     
31207     getScaleLevel : function()
31208     {
31209         return this.baseScale * Math.pow(1.1, this.scale);
31210     },
31211     
31212     onTouchStart : function(e)
31213     {
31214         if(!this.canvasLoaded){
31215             this.beforeSelectFile(e);
31216             return;
31217         }
31218         
31219         var touches = e.browserEvent.touches;
31220         
31221         if(!touches){
31222             return;
31223         }
31224         
31225         if(touches.length == 1){
31226             this.onMouseDown(e);
31227             return;
31228         }
31229         
31230         if(touches.length != 2){
31231             return;
31232         }
31233         
31234         var coords = [];
31235         
31236         for(var i = 0, finger; finger = touches[i]; i++){
31237             coords.push(finger.pageX, finger.pageY);
31238         }
31239         
31240         var x = Math.pow(coords[0] - coords[2], 2);
31241         var y = Math.pow(coords[1] - coords[3], 2);
31242         
31243         this.startDistance = Math.sqrt(x + y);
31244         
31245         this.startScale = this.scale;
31246         
31247         this.pinching = true;
31248         this.dragable = false;
31249         
31250     },
31251     
31252     onTouchMove : function(e)
31253     {
31254         if(!this.pinching && !this.dragable){
31255             return;
31256         }
31257         
31258         var touches = e.browserEvent.touches;
31259         
31260         if(!touches){
31261             return;
31262         }
31263         
31264         if(this.dragable){
31265             this.onMouseMove(e);
31266             return;
31267         }
31268         
31269         var coords = [];
31270         
31271         for(var i = 0, finger; finger = touches[i]; i++){
31272             coords.push(finger.pageX, finger.pageY);
31273         }
31274         
31275         var x = Math.pow(coords[0] - coords[2], 2);
31276         var y = Math.pow(coords[1] - coords[3], 2);
31277         
31278         this.endDistance = Math.sqrt(x + y);
31279         
31280         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31281         
31282         if(!this.zoomable()){
31283             this.scale = this.startScale;
31284             return;
31285         }
31286         
31287         this.draw();
31288         
31289     },
31290     
31291     onTouchEnd : function(e)
31292     {
31293         this.pinching = false;
31294         this.dragable = false;
31295         
31296     },
31297     
31298     process : function(file, crop)
31299     {
31300         if(this.loadMask){
31301             this.maskEl.mask(this.loadingText);
31302         }
31303         
31304         this.xhr = new XMLHttpRequest();
31305         
31306         file.xhr = this.xhr;
31307
31308         this.xhr.open(this.method, this.url, true);
31309         
31310         var headers = {
31311             "Accept": "application/json",
31312             "Cache-Control": "no-cache",
31313             "X-Requested-With": "XMLHttpRequest"
31314         };
31315         
31316         for (var headerName in headers) {
31317             var headerValue = headers[headerName];
31318             if (headerValue) {
31319                 this.xhr.setRequestHeader(headerName, headerValue);
31320             }
31321         }
31322         
31323         var _this = this;
31324         
31325         this.xhr.onload = function()
31326         {
31327             _this.xhrOnLoad(_this.xhr);
31328         }
31329         
31330         this.xhr.onerror = function()
31331         {
31332             _this.xhrOnError(_this.xhr);
31333         }
31334         
31335         var formData = new FormData();
31336
31337         formData.append('returnHTML', 'NO');
31338         
31339         if(crop){
31340             formData.append('crop', crop);
31341         }
31342         
31343         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31344             formData.append(this.paramName, file, file.name);
31345         }
31346         
31347         if(typeof(file.filename) != 'undefined'){
31348             formData.append('filename', file.filename);
31349         }
31350         
31351         if(typeof(file.mimetype) != 'undefined'){
31352             formData.append('mimetype', file.mimetype);
31353         }
31354         
31355         if(this.fireEvent('arrange', this, formData) != false){
31356             this.xhr.send(formData);
31357         };
31358     },
31359     
31360     xhrOnLoad : function(xhr)
31361     {
31362         if(this.loadMask){
31363             this.maskEl.unmask();
31364         }
31365         
31366         if (xhr.readyState !== 4) {
31367             this.fireEvent('exception', this, xhr);
31368             return;
31369         }
31370
31371         var response = Roo.decode(xhr.responseText);
31372         
31373         if(!response.success){
31374             this.fireEvent('exception', this, xhr);
31375             return;
31376         }
31377         
31378         var response = Roo.decode(xhr.responseText);
31379         
31380         this.fireEvent('upload', this, response);
31381         
31382     },
31383     
31384     xhrOnError : function()
31385     {
31386         if(this.loadMask){
31387             this.maskEl.unmask();
31388         }
31389         
31390         Roo.log('xhr on error');
31391         
31392         var response = Roo.decode(xhr.responseText);
31393           
31394         Roo.log(response);
31395         
31396     },
31397     
31398     prepare : function(file)
31399     {   
31400         if(this.loadMask){
31401             this.maskEl.mask(this.loadingText);
31402         }
31403         
31404         this.file = false;
31405         this.exif = {};
31406         
31407         if(typeof(file) === 'string'){
31408             this.loadCanvas(file);
31409             return;
31410         }
31411         
31412         if(!file || !this.urlAPI){
31413             return;
31414         }
31415         
31416         this.file = file;
31417         this.cropType = file.type;
31418         
31419         var _this = this;
31420         
31421         if(this.fireEvent('prepare', this, this.file) != false){
31422             
31423             var reader = new FileReader();
31424             
31425             reader.onload = function (e) {
31426                 if (e.target.error) {
31427                     Roo.log(e.target.error);
31428                     return;
31429                 }
31430                 
31431                 var buffer = e.target.result,
31432                     dataView = new DataView(buffer),
31433                     offset = 2,
31434                     maxOffset = dataView.byteLength - 4,
31435                     markerBytes,
31436                     markerLength;
31437                 
31438                 if (dataView.getUint16(0) === 0xffd8) {
31439                     while (offset < maxOffset) {
31440                         markerBytes = dataView.getUint16(offset);
31441                         
31442                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31443                             markerLength = dataView.getUint16(offset + 2) + 2;
31444                             if (offset + markerLength > dataView.byteLength) {
31445                                 Roo.log('Invalid meta data: Invalid segment size.');
31446                                 break;
31447                             }
31448                             
31449                             if(markerBytes == 0xffe1){
31450                                 _this.parseExifData(
31451                                     dataView,
31452                                     offset,
31453                                     markerLength
31454                                 );
31455                             }
31456                             
31457                             offset += markerLength;
31458                             
31459                             continue;
31460                         }
31461                         
31462                         break;
31463                     }
31464                     
31465                 }
31466                 
31467                 var url = _this.urlAPI.createObjectURL(_this.file);
31468                 
31469                 _this.loadCanvas(url);
31470                 
31471                 return;
31472             }
31473             
31474             reader.readAsArrayBuffer(this.file);
31475             
31476         }
31477         
31478     },
31479     
31480     parseExifData : function(dataView, offset, length)
31481     {
31482         var tiffOffset = offset + 10,
31483             littleEndian,
31484             dirOffset;
31485     
31486         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31487             // No Exif data, might be XMP data instead
31488             return;
31489         }
31490         
31491         // Check for the ASCII code for "Exif" (0x45786966):
31492         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31493             // No Exif data, might be XMP data instead
31494             return;
31495         }
31496         if (tiffOffset + 8 > dataView.byteLength) {
31497             Roo.log('Invalid Exif data: Invalid segment size.');
31498             return;
31499         }
31500         // Check for the two null bytes:
31501         if (dataView.getUint16(offset + 8) !== 0x0000) {
31502             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31503             return;
31504         }
31505         // Check the byte alignment:
31506         switch (dataView.getUint16(tiffOffset)) {
31507         case 0x4949:
31508             littleEndian = true;
31509             break;
31510         case 0x4D4D:
31511             littleEndian = false;
31512             break;
31513         default:
31514             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31515             return;
31516         }
31517         // Check for the TIFF tag marker (0x002A):
31518         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31519             Roo.log('Invalid Exif data: Missing TIFF marker.');
31520             return;
31521         }
31522         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31523         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31524         
31525         this.parseExifTags(
31526             dataView,
31527             tiffOffset,
31528             tiffOffset + dirOffset,
31529             littleEndian
31530         );
31531     },
31532     
31533     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31534     {
31535         var tagsNumber,
31536             dirEndOffset,
31537             i;
31538         if (dirOffset + 6 > dataView.byteLength) {
31539             Roo.log('Invalid Exif data: Invalid directory offset.');
31540             return;
31541         }
31542         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31543         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31544         if (dirEndOffset + 4 > dataView.byteLength) {
31545             Roo.log('Invalid Exif data: Invalid directory size.');
31546             return;
31547         }
31548         for (i = 0; i < tagsNumber; i += 1) {
31549             this.parseExifTag(
31550                 dataView,
31551                 tiffOffset,
31552                 dirOffset + 2 + 12 * i, // tag offset
31553                 littleEndian
31554             );
31555         }
31556         // Return the offset to the next directory:
31557         return dataView.getUint32(dirEndOffset, littleEndian);
31558     },
31559     
31560     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31561     {
31562         var tag = dataView.getUint16(offset, littleEndian);
31563         
31564         this.exif[tag] = this.getExifValue(
31565             dataView,
31566             tiffOffset,
31567             offset,
31568             dataView.getUint16(offset + 2, littleEndian), // tag type
31569             dataView.getUint32(offset + 4, littleEndian), // tag length
31570             littleEndian
31571         );
31572     },
31573     
31574     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31575     {
31576         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31577             tagSize,
31578             dataOffset,
31579             values,
31580             i,
31581             str,
31582             c;
31583     
31584         if (!tagType) {
31585             Roo.log('Invalid Exif data: Invalid tag type.');
31586             return;
31587         }
31588         
31589         tagSize = tagType.size * length;
31590         // Determine if the value is contained in the dataOffset bytes,
31591         // or if the value at the dataOffset is a pointer to the actual data:
31592         dataOffset = tagSize > 4 ?
31593                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31594         if (dataOffset + tagSize > dataView.byteLength) {
31595             Roo.log('Invalid Exif data: Invalid data offset.');
31596             return;
31597         }
31598         if (length === 1) {
31599             return tagType.getValue(dataView, dataOffset, littleEndian);
31600         }
31601         values = [];
31602         for (i = 0; i < length; i += 1) {
31603             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31604         }
31605         
31606         if (tagType.ascii) {
31607             str = '';
31608             // Concatenate the chars:
31609             for (i = 0; i < values.length; i += 1) {
31610                 c = values[i];
31611                 // Ignore the terminating NULL byte(s):
31612                 if (c === '\u0000') {
31613                     break;
31614                 }
31615                 str += c;
31616             }
31617             return str;
31618         }
31619         return values;
31620     }
31621     
31622 });
31623
31624 Roo.apply(Roo.bootstrap.UploadCropbox, {
31625     tags : {
31626         'Orientation': 0x0112
31627     },
31628     
31629     Orientation: {
31630             1: 0, //'top-left',
31631 //            2: 'top-right',
31632             3: 180, //'bottom-right',
31633 //            4: 'bottom-left',
31634 //            5: 'left-top',
31635             6: 90, //'right-top',
31636 //            7: 'right-bottom',
31637             8: 270 //'left-bottom'
31638     },
31639     
31640     exifTagTypes : {
31641         // byte, 8-bit unsigned int:
31642         1: {
31643             getValue: function (dataView, dataOffset) {
31644                 return dataView.getUint8(dataOffset);
31645             },
31646             size: 1
31647         },
31648         // ascii, 8-bit byte:
31649         2: {
31650             getValue: function (dataView, dataOffset) {
31651                 return String.fromCharCode(dataView.getUint8(dataOffset));
31652             },
31653             size: 1,
31654             ascii: true
31655         },
31656         // short, 16 bit int:
31657         3: {
31658             getValue: function (dataView, dataOffset, littleEndian) {
31659                 return dataView.getUint16(dataOffset, littleEndian);
31660             },
31661             size: 2
31662         },
31663         // long, 32 bit int:
31664         4: {
31665             getValue: function (dataView, dataOffset, littleEndian) {
31666                 return dataView.getUint32(dataOffset, littleEndian);
31667             },
31668             size: 4
31669         },
31670         // rational = two long values, first is numerator, second is denominator:
31671         5: {
31672             getValue: function (dataView, dataOffset, littleEndian) {
31673                 return dataView.getUint32(dataOffset, littleEndian) /
31674                     dataView.getUint32(dataOffset + 4, littleEndian);
31675             },
31676             size: 8
31677         },
31678         // slong, 32 bit signed int:
31679         9: {
31680             getValue: function (dataView, dataOffset, littleEndian) {
31681                 return dataView.getInt32(dataOffset, littleEndian);
31682             },
31683             size: 4
31684         },
31685         // srational, two slongs, first is numerator, second is denominator:
31686         10: {
31687             getValue: function (dataView, dataOffset, littleEndian) {
31688                 return dataView.getInt32(dataOffset, littleEndian) /
31689                     dataView.getInt32(dataOffset + 4, littleEndian);
31690             },
31691             size: 8
31692         }
31693     },
31694     
31695     footer : {
31696         STANDARD : [
31697             {
31698                 tag : 'div',
31699                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31700                 action : 'rotate-left',
31701                 cn : [
31702                     {
31703                         tag : 'button',
31704                         cls : 'btn btn-default',
31705                         html : '<i class="fa fa-undo"></i>'
31706                     }
31707                 ]
31708             },
31709             {
31710                 tag : 'div',
31711                 cls : 'btn-group roo-upload-cropbox-picture',
31712                 action : 'picture',
31713                 cn : [
31714                     {
31715                         tag : 'button',
31716                         cls : 'btn btn-default',
31717                         html : '<i class="fa fa-picture-o"></i>'
31718                     }
31719                 ]
31720             },
31721             {
31722                 tag : 'div',
31723                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724                 action : 'rotate-right',
31725                 cn : [
31726                     {
31727                         tag : 'button',
31728                         cls : 'btn btn-default',
31729                         html : '<i class="fa fa-repeat"></i>'
31730                     }
31731                 ]
31732             }
31733         ],
31734         DOCUMENT : [
31735             {
31736                 tag : 'div',
31737                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31738                 action : 'rotate-left',
31739                 cn : [
31740                     {
31741                         tag : 'button',
31742                         cls : 'btn btn-default',
31743                         html : '<i class="fa fa-undo"></i>'
31744                     }
31745                 ]
31746             },
31747             {
31748                 tag : 'div',
31749                 cls : 'btn-group roo-upload-cropbox-download',
31750                 action : 'download',
31751                 cn : [
31752                     {
31753                         tag : 'button',
31754                         cls : 'btn btn-default',
31755                         html : '<i class="fa fa-download"></i>'
31756                     }
31757                 ]
31758             },
31759             {
31760                 tag : 'div',
31761                 cls : 'btn-group roo-upload-cropbox-crop',
31762                 action : 'crop',
31763                 cn : [
31764                     {
31765                         tag : 'button',
31766                         cls : 'btn btn-default',
31767                         html : '<i class="fa fa-crop"></i>'
31768                     }
31769                 ]
31770             },
31771             {
31772                 tag : 'div',
31773                 cls : 'btn-group roo-upload-cropbox-trash',
31774                 action : 'trash',
31775                 cn : [
31776                     {
31777                         tag : 'button',
31778                         cls : 'btn btn-default',
31779                         html : '<i class="fa fa-trash"></i>'
31780                     }
31781                 ]
31782             },
31783             {
31784                 tag : 'div',
31785                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31786                 action : 'rotate-right',
31787                 cn : [
31788                     {
31789                         tag : 'button',
31790                         cls : 'btn btn-default',
31791                         html : '<i class="fa fa-repeat"></i>'
31792                     }
31793                 ]
31794             }
31795         ],
31796         ROTATOR : [
31797             {
31798                 tag : 'div',
31799                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31800                 action : 'rotate-left',
31801                 cn : [
31802                     {
31803                         tag : 'button',
31804                         cls : 'btn btn-default',
31805                         html : '<i class="fa fa-undo"></i>'
31806                     }
31807                 ]
31808             },
31809             {
31810                 tag : 'div',
31811                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31812                 action : 'rotate-right',
31813                 cn : [
31814                     {
31815                         tag : 'button',
31816                         cls : 'btn btn-default',
31817                         html : '<i class="fa fa-repeat"></i>'
31818                     }
31819                 ]
31820             }
31821         ]
31822     }
31823 });
31824
31825 /*
31826 * Licence: LGPL
31827 */
31828
31829 /**
31830  * @class Roo.bootstrap.DocumentManager
31831  * @extends Roo.bootstrap.Component
31832  * Bootstrap DocumentManager class
31833  * @cfg {String} paramName default 'imageUpload'
31834  * @cfg {String} toolTipName default 'filename'
31835  * @cfg {String} method default POST
31836  * @cfg {String} url action url
31837  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31838  * @cfg {Boolean} multiple multiple upload default true
31839  * @cfg {Number} thumbSize default 300
31840  * @cfg {String} fieldLabel
31841  * @cfg {Number} labelWidth default 4
31842  * @cfg {String} labelAlign (left|top) default left
31843  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31844 * @cfg {Number} labellg set the width of label (1-12)
31845  * @cfg {Number} labelmd set the width of label (1-12)
31846  * @cfg {Number} labelsm set the width of label (1-12)
31847  * @cfg {Number} labelxs set the width of label (1-12)
31848  * 
31849  * @constructor
31850  * Create a new DocumentManager
31851  * @param {Object} config The config object
31852  */
31853
31854 Roo.bootstrap.DocumentManager = function(config){
31855     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31856     
31857     this.files = [];
31858     this.delegates = [];
31859     
31860     this.addEvents({
31861         /**
31862          * @event initial
31863          * Fire when initial the DocumentManager
31864          * @param {Roo.bootstrap.DocumentManager} this
31865          */
31866         "initial" : true,
31867         /**
31868          * @event inspect
31869          * inspect selected file
31870          * @param {Roo.bootstrap.DocumentManager} this
31871          * @param {File} file
31872          */
31873         "inspect" : true,
31874         /**
31875          * @event exception
31876          * Fire when xhr load exception
31877          * @param {Roo.bootstrap.DocumentManager} this
31878          * @param {XMLHttpRequest} xhr
31879          */
31880         "exception" : true,
31881         /**
31882          * @event afterupload
31883          * Fire when xhr load exception
31884          * @param {Roo.bootstrap.DocumentManager} this
31885          * @param {XMLHttpRequest} xhr
31886          */
31887         "afterupload" : true,
31888         /**
31889          * @event prepare
31890          * prepare the form data
31891          * @param {Roo.bootstrap.DocumentManager} this
31892          * @param {Object} formData
31893          */
31894         "prepare" : true,
31895         /**
31896          * @event remove
31897          * Fire when remove the file
31898          * @param {Roo.bootstrap.DocumentManager} this
31899          * @param {Object} file
31900          */
31901         "remove" : true,
31902         /**
31903          * @event refresh
31904          * Fire after refresh the file
31905          * @param {Roo.bootstrap.DocumentManager} this
31906          */
31907         "refresh" : true,
31908         /**
31909          * @event click
31910          * Fire after click the image
31911          * @param {Roo.bootstrap.DocumentManager} this
31912          * @param {Object} file
31913          */
31914         "click" : true,
31915         /**
31916          * @event edit
31917          * Fire when upload a image and editable set to true
31918          * @param {Roo.bootstrap.DocumentManager} this
31919          * @param {Object} file
31920          */
31921         "edit" : true,
31922         /**
31923          * @event beforeselectfile
31924          * Fire before select file
31925          * @param {Roo.bootstrap.DocumentManager} this
31926          */
31927         "beforeselectfile" : true,
31928         /**
31929          * @event process
31930          * Fire before process file
31931          * @param {Roo.bootstrap.DocumentManager} this
31932          * @param {Object} file
31933          */
31934         "process" : true,
31935         /**
31936          * @event previewrendered
31937          * Fire when preview rendered
31938          * @param {Roo.bootstrap.DocumentManager} this
31939          * @param {Object} file
31940          */
31941         "previewrendered" : true,
31942         /**
31943          */
31944         "previewResize" : true
31945         
31946     });
31947 };
31948
31949 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31950     
31951     boxes : 0,
31952     inputName : '',
31953     thumbSize : 300,
31954     multiple : true,
31955     files : false,
31956     method : 'POST',
31957     url : '',
31958     paramName : 'imageUpload',
31959     toolTipName : 'filename',
31960     fieldLabel : '',
31961     labelWidth : 4,
31962     labelAlign : 'left',
31963     editable : true,
31964     delegates : false,
31965     xhr : false, 
31966     
31967     labellg : 0,
31968     labelmd : 0,
31969     labelsm : 0,
31970     labelxs : 0,
31971     
31972     getAutoCreate : function()
31973     {   
31974         var managerWidget = {
31975             tag : 'div',
31976             cls : 'roo-document-manager',
31977             cn : [
31978                 {
31979                     tag : 'input',
31980                     cls : 'roo-document-manager-selector',
31981                     type : 'file'
31982                 },
31983                 {
31984                     tag : 'div',
31985                     cls : 'roo-document-manager-uploader',
31986                     cn : [
31987                         {
31988                             tag : 'div',
31989                             cls : 'roo-document-manager-upload-btn',
31990                             html : '<i class="fa fa-plus"></i>'
31991                         }
31992                     ]
31993                     
31994                 }
31995             ]
31996         };
31997         
31998         var content = [
31999             {
32000                 tag : 'div',
32001                 cls : 'column col-md-12',
32002                 cn : managerWidget
32003             }
32004         ];
32005         
32006         if(this.fieldLabel.length){
32007             
32008             content = [
32009                 {
32010                     tag : 'div',
32011                     cls : 'column col-md-12',
32012                     html : this.fieldLabel
32013                 },
32014                 {
32015                     tag : 'div',
32016                     cls : 'column col-md-12',
32017                     cn : managerWidget
32018                 }
32019             ];
32020
32021             if(this.labelAlign == 'left'){
32022                 content = [
32023                     {
32024                         tag : 'div',
32025                         cls : 'column',
32026                         html : this.fieldLabel
32027                     },
32028                     {
32029                         tag : 'div',
32030                         cls : 'column',
32031                         cn : managerWidget
32032                     }
32033                 ];
32034                 
32035                 if(this.labelWidth > 12){
32036                     content[0].style = "width: " + this.labelWidth + 'px';
32037                 }
32038
32039                 if(this.labelWidth < 13 && this.labelmd == 0){
32040                     this.labelmd = this.labelWidth;
32041                 }
32042
32043                 if(this.labellg > 0){
32044                     content[0].cls += ' col-lg-' + this.labellg;
32045                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32046                 }
32047
32048                 if(this.labelmd > 0){
32049                     content[0].cls += ' col-md-' + this.labelmd;
32050                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32051                 }
32052
32053                 if(this.labelsm > 0){
32054                     content[0].cls += ' col-sm-' + this.labelsm;
32055                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32056                 }
32057
32058                 if(this.labelxs > 0){
32059                     content[0].cls += ' col-xs-' + this.labelxs;
32060                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32061                 }
32062                 
32063             }
32064         }
32065         
32066         var cfg = {
32067             tag : 'div',
32068             cls : 'row clearfix',
32069             cn : content
32070         };
32071         
32072         return cfg;
32073         
32074     },
32075     
32076     initEvents : function()
32077     {
32078         this.managerEl = this.el.select('.roo-document-manager', true).first();
32079         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32080         
32081         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32082         this.selectorEl.hide();
32083         
32084         if(this.multiple){
32085             this.selectorEl.attr('multiple', 'multiple');
32086         }
32087         
32088         this.selectorEl.on('change', this.onFileSelected, this);
32089         
32090         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32091         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32092         
32093         this.uploader.on('click', this.onUploaderClick, this);
32094         
32095         this.renderProgressDialog();
32096         
32097         var _this = this;
32098         
32099         window.addEventListener("resize", function() { _this.refresh(); } );
32100         
32101         this.fireEvent('initial', this);
32102     },
32103     
32104     renderProgressDialog : function()
32105     {
32106         var _this = this;
32107         
32108         this.progressDialog = new Roo.bootstrap.Modal({
32109             cls : 'roo-document-manager-progress-dialog',
32110             allow_close : false,
32111             animate : false,
32112             title : '',
32113             buttons : [
32114                 {
32115                     name  :'cancel',
32116                     weight : 'danger',
32117                     html : 'Cancel'
32118                 }
32119             ], 
32120             listeners : { 
32121                 btnclick : function() {
32122                     _this.uploadCancel();
32123                     this.hide();
32124                 }
32125             }
32126         });
32127          
32128         this.progressDialog.render(Roo.get(document.body));
32129          
32130         this.progress = new Roo.bootstrap.Progress({
32131             cls : 'roo-document-manager-progress',
32132             active : true,
32133             striped : true
32134         });
32135         
32136         this.progress.render(this.progressDialog.getChildContainer());
32137         
32138         this.progressBar = new Roo.bootstrap.ProgressBar({
32139             cls : 'roo-document-manager-progress-bar',
32140             aria_valuenow : 0,
32141             aria_valuemin : 0,
32142             aria_valuemax : 12,
32143             panel : 'success'
32144         });
32145         
32146         this.progressBar.render(this.progress.getChildContainer());
32147     },
32148     
32149     onUploaderClick : function(e)
32150     {
32151         e.preventDefault();
32152      
32153         if(this.fireEvent('beforeselectfile', this) != false){
32154             this.selectorEl.dom.click();
32155         }
32156         
32157     },
32158     
32159     onFileSelected : function(e)
32160     {
32161         e.preventDefault();
32162         
32163         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32164             return;
32165         }
32166         
32167         Roo.each(this.selectorEl.dom.files, function(file){
32168             if(this.fireEvent('inspect', this, file) != false){
32169                 this.files.push(file);
32170             }
32171         }, this);
32172         
32173         this.queue();
32174         
32175     },
32176     
32177     queue : function()
32178     {
32179         this.selectorEl.dom.value = '';
32180         
32181         if(!this.files || !this.files.length){
32182             return;
32183         }
32184         
32185         if(this.boxes > 0 && this.files.length > this.boxes){
32186             this.files = this.files.slice(0, this.boxes);
32187         }
32188         
32189         this.uploader.show();
32190         
32191         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32192             this.uploader.hide();
32193         }
32194         
32195         var _this = this;
32196         
32197         var files = [];
32198         
32199         var docs = [];
32200         
32201         Roo.each(this.files, function(file){
32202             
32203             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32204                 var f = this.renderPreview(file);
32205                 files.push(f);
32206                 return;
32207             }
32208             
32209             if(file.type.indexOf('image') != -1){
32210                 this.delegates.push(
32211                     (function(){
32212                         _this.process(file);
32213                     }).createDelegate(this)
32214                 );
32215         
32216                 return;
32217             }
32218             
32219             docs.push(
32220                 (function(){
32221                     _this.process(file);
32222                 }).createDelegate(this)
32223             );
32224             
32225         }, this);
32226         
32227         this.files = files;
32228         
32229         this.delegates = this.delegates.concat(docs);
32230         
32231         if(!this.delegates.length){
32232             this.refresh();
32233             return;
32234         }
32235         
32236         this.progressBar.aria_valuemax = this.delegates.length;
32237         
32238         this.arrange();
32239         
32240         return;
32241     },
32242     
32243     arrange : function()
32244     {
32245         if(!this.delegates.length){
32246             this.progressDialog.hide();
32247             this.refresh();
32248             return;
32249         }
32250         
32251         var delegate = this.delegates.shift();
32252         
32253         this.progressDialog.show();
32254         
32255         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32256         
32257         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32258         
32259         delegate();
32260     },
32261     
32262     refresh : function()
32263     {
32264         this.uploader.show();
32265         
32266         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32267             this.uploader.hide();
32268         }
32269         
32270         Roo.isTouch ? this.closable(false) : this.closable(true);
32271         
32272         this.fireEvent('refresh', this);
32273     },
32274     
32275     onRemove : function(e, el, o)
32276     {
32277         e.preventDefault();
32278         
32279         this.fireEvent('remove', this, o);
32280         
32281     },
32282     
32283     remove : function(o)
32284     {
32285         var files = [];
32286         
32287         Roo.each(this.files, function(file){
32288             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32289                 files.push(file);
32290                 return;
32291             }
32292
32293             o.target.remove();
32294
32295         }, this);
32296         
32297         this.files = files;
32298         
32299         this.refresh();
32300     },
32301     
32302     clear : function()
32303     {
32304         Roo.each(this.files, function(file){
32305             if(!file.target){
32306                 return;
32307             }
32308             
32309             file.target.remove();
32310
32311         }, this);
32312         
32313         this.files = [];
32314         
32315         this.refresh();
32316     },
32317     
32318     onClick : function(e, el, o)
32319     {
32320         e.preventDefault();
32321         
32322         this.fireEvent('click', this, o);
32323         
32324     },
32325     
32326     closable : function(closable)
32327     {
32328         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32329             
32330             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32331             
32332             if(closable){
32333                 el.show();
32334                 return;
32335             }
32336             
32337             el.hide();
32338             
32339         }, this);
32340     },
32341     
32342     xhrOnLoad : function(xhr)
32343     {
32344         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32345             el.remove();
32346         }, this);
32347         
32348         if (xhr.readyState !== 4) {
32349             this.arrange();
32350             this.fireEvent('exception', this, xhr);
32351             return;
32352         }
32353
32354         var response = Roo.decode(xhr.responseText);
32355         
32356         if(!response.success){
32357             this.arrange();
32358             this.fireEvent('exception', this, xhr);
32359             return;
32360         }
32361         
32362         var file = this.renderPreview(response.data);
32363         
32364         this.files.push(file);
32365         
32366         this.arrange();
32367         
32368         this.fireEvent('afterupload', this, xhr);
32369         
32370     },
32371     
32372     xhrOnError : function(xhr)
32373     {
32374         Roo.log('xhr on error');
32375         
32376         var response = Roo.decode(xhr.responseText);
32377           
32378         Roo.log(response);
32379         
32380         this.arrange();
32381     },
32382     
32383     process : function(file)
32384     {
32385         if(this.fireEvent('process', this, file) !== false){
32386             if(this.editable && file.type.indexOf('image') != -1){
32387                 this.fireEvent('edit', this, file);
32388                 return;
32389             }
32390
32391             this.uploadStart(file, false);
32392
32393             return;
32394         }
32395         
32396     },
32397     
32398     uploadStart : function(file, crop)
32399     {
32400         this.xhr = new XMLHttpRequest();
32401         
32402         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32403             this.arrange();
32404             return;
32405         }
32406         
32407         file.xhr = this.xhr;
32408             
32409         this.managerEl.createChild({
32410             tag : 'div',
32411             cls : 'roo-document-manager-loading',
32412             cn : [
32413                 {
32414                     tag : 'div',
32415                     tooltip : file.name,
32416                     cls : 'roo-document-manager-thumb',
32417                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32418                 }
32419             ]
32420
32421         });
32422
32423         this.xhr.open(this.method, this.url, true);
32424         
32425         var headers = {
32426             "Accept": "application/json",
32427             "Cache-Control": "no-cache",
32428             "X-Requested-With": "XMLHttpRequest"
32429         };
32430         
32431         for (var headerName in headers) {
32432             var headerValue = headers[headerName];
32433             if (headerValue) {
32434                 this.xhr.setRequestHeader(headerName, headerValue);
32435             }
32436         }
32437         
32438         var _this = this;
32439         
32440         this.xhr.onload = function()
32441         {
32442             _this.xhrOnLoad(_this.xhr);
32443         }
32444         
32445         this.xhr.onerror = function()
32446         {
32447             _this.xhrOnError(_this.xhr);
32448         }
32449         
32450         var formData = new FormData();
32451
32452         formData.append('returnHTML', 'NO');
32453         
32454         if(crop){
32455             formData.append('crop', crop);
32456         }
32457         
32458         formData.append(this.paramName, file, file.name);
32459         
32460         var options = {
32461             file : file, 
32462             manually : false
32463         };
32464         
32465         if(this.fireEvent('prepare', this, formData, options) != false){
32466             
32467             if(options.manually){
32468                 return;
32469             }
32470             
32471             this.xhr.send(formData);
32472             return;
32473         };
32474         
32475         this.uploadCancel();
32476     },
32477     
32478     uploadCancel : function()
32479     {
32480         if (this.xhr) {
32481             this.xhr.abort();
32482         }
32483         
32484         this.delegates = [];
32485         
32486         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32487             el.remove();
32488         }, this);
32489         
32490         this.arrange();
32491     },
32492     
32493     renderPreview : function(file)
32494     {
32495         if(typeof(file.target) != 'undefined' && file.target){
32496             return file;
32497         }
32498         
32499         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32500         
32501         var previewEl = this.managerEl.createChild({
32502             tag : 'div',
32503             cls : 'roo-document-manager-preview',
32504             cn : [
32505                 {
32506                     tag : 'div',
32507                     tooltip : file[this.toolTipName],
32508                     cls : 'roo-document-manager-thumb',
32509                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32510                 },
32511                 {
32512                     tag : 'button',
32513                     cls : 'close',
32514                     html : '<i class="fa fa-times-circle"></i>'
32515                 }
32516             ]
32517         });
32518
32519         var close = previewEl.select('button.close', true).first();
32520
32521         close.on('click', this.onRemove, this, file);
32522
32523         file.target = previewEl;
32524
32525         var image = previewEl.select('img', true).first();
32526         
32527         var _this = this;
32528         
32529         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32530         
32531         image.on('click', this.onClick, this, file);
32532         
32533         this.fireEvent('previewrendered', this, file);
32534         
32535         return file;
32536         
32537     },
32538     
32539     onPreviewLoad : function(file, image)
32540     {
32541         if(typeof(file.target) == 'undefined' || !file.target){
32542             return;
32543         }
32544         
32545         var width = image.dom.naturalWidth || image.dom.width;
32546         var height = image.dom.naturalHeight || image.dom.height;
32547         
32548         if(!this.previewResize) {
32549             return;
32550         }
32551         
32552         if(width > height){
32553             file.target.addClass('wide');
32554             return;
32555         }
32556         
32557         file.target.addClass('tall');
32558         return;
32559         
32560     },
32561     
32562     uploadFromSource : function(file, crop)
32563     {
32564         this.xhr = new XMLHttpRequest();
32565         
32566         this.managerEl.createChild({
32567             tag : 'div',
32568             cls : 'roo-document-manager-loading',
32569             cn : [
32570                 {
32571                     tag : 'div',
32572                     tooltip : file.name,
32573                     cls : 'roo-document-manager-thumb',
32574                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32575                 }
32576             ]
32577
32578         });
32579
32580         this.xhr.open(this.method, this.url, true);
32581         
32582         var headers = {
32583             "Accept": "application/json",
32584             "Cache-Control": "no-cache",
32585             "X-Requested-With": "XMLHttpRequest"
32586         };
32587         
32588         for (var headerName in headers) {
32589             var headerValue = headers[headerName];
32590             if (headerValue) {
32591                 this.xhr.setRequestHeader(headerName, headerValue);
32592             }
32593         }
32594         
32595         var _this = this;
32596         
32597         this.xhr.onload = function()
32598         {
32599             _this.xhrOnLoad(_this.xhr);
32600         }
32601         
32602         this.xhr.onerror = function()
32603         {
32604             _this.xhrOnError(_this.xhr);
32605         }
32606         
32607         var formData = new FormData();
32608
32609         formData.append('returnHTML', 'NO');
32610         
32611         formData.append('crop', crop);
32612         
32613         if(typeof(file.filename) != 'undefined'){
32614             formData.append('filename', file.filename);
32615         }
32616         
32617         if(typeof(file.mimetype) != 'undefined'){
32618             formData.append('mimetype', file.mimetype);
32619         }
32620         
32621         Roo.log(formData);
32622         
32623         if(this.fireEvent('prepare', this, formData) != false){
32624             this.xhr.send(formData);
32625         };
32626     }
32627 });
32628
32629 /*
32630 * Licence: LGPL
32631 */
32632
32633 /**
32634  * @class Roo.bootstrap.DocumentViewer
32635  * @extends Roo.bootstrap.Component
32636  * Bootstrap DocumentViewer class
32637  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32638  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32639  * 
32640  * @constructor
32641  * Create a new DocumentViewer
32642  * @param {Object} config The config object
32643  */
32644
32645 Roo.bootstrap.DocumentViewer = function(config){
32646     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32647     
32648     this.addEvents({
32649         /**
32650          * @event initial
32651          * Fire after initEvent
32652          * @param {Roo.bootstrap.DocumentViewer} this
32653          */
32654         "initial" : true,
32655         /**
32656          * @event click
32657          * Fire after click
32658          * @param {Roo.bootstrap.DocumentViewer} this
32659          */
32660         "click" : true,
32661         /**
32662          * @event download
32663          * Fire after download button
32664          * @param {Roo.bootstrap.DocumentViewer} this
32665          */
32666         "download" : true,
32667         /**
32668          * @event trash
32669          * Fire after trash button
32670          * @param {Roo.bootstrap.DocumentViewer} this
32671          */
32672         "trash" : true
32673         
32674     });
32675 };
32676
32677 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32678     
32679     showDownload : true,
32680     
32681     showTrash : true,
32682     
32683     getAutoCreate : function()
32684     {
32685         var cfg = {
32686             tag : 'div',
32687             cls : 'roo-document-viewer',
32688             cn : [
32689                 {
32690                     tag : 'div',
32691                     cls : 'roo-document-viewer-body',
32692                     cn : [
32693                         {
32694                             tag : 'div',
32695                             cls : 'roo-document-viewer-thumb',
32696                             cn : [
32697                                 {
32698                                     tag : 'img',
32699                                     cls : 'roo-document-viewer-image'
32700                                 }
32701                             ]
32702                         }
32703                     ]
32704                 },
32705                 {
32706                     tag : 'div',
32707                     cls : 'roo-document-viewer-footer',
32708                     cn : {
32709                         tag : 'div',
32710                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32711                         cn : [
32712                             {
32713                                 tag : 'div',
32714                                 cls : 'btn-group roo-document-viewer-download',
32715                                 cn : [
32716                                     {
32717                                         tag : 'button',
32718                                         cls : 'btn btn-default',
32719                                         html : '<i class="fa fa-download"></i>'
32720                                     }
32721                                 ]
32722                             },
32723                             {
32724                                 tag : 'div',
32725                                 cls : 'btn-group roo-document-viewer-trash',
32726                                 cn : [
32727                                     {
32728                                         tag : 'button',
32729                                         cls : 'btn btn-default',
32730                                         html : '<i class="fa fa-trash"></i>'
32731                                     }
32732                                 ]
32733                             }
32734                         ]
32735                     }
32736                 }
32737             ]
32738         };
32739         
32740         return cfg;
32741     },
32742     
32743     initEvents : function()
32744     {
32745         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32746         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32747         
32748         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32749         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32750         
32751         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32752         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32753         
32754         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32755         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32756         
32757         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32758         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32759         
32760         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32761         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32762         
32763         this.bodyEl.on('click', this.onClick, this);
32764         this.downloadBtn.on('click', this.onDownload, this);
32765         this.trashBtn.on('click', this.onTrash, this);
32766         
32767         this.downloadBtn.hide();
32768         this.trashBtn.hide();
32769         
32770         if(this.showDownload){
32771             this.downloadBtn.show();
32772         }
32773         
32774         if(this.showTrash){
32775             this.trashBtn.show();
32776         }
32777         
32778         if(!this.showDownload && !this.showTrash) {
32779             this.footerEl.hide();
32780         }
32781         
32782     },
32783     
32784     initial : function()
32785     {
32786         this.fireEvent('initial', this);
32787         
32788     },
32789     
32790     onClick : function(e)
32791     {
32792         e.preventDefault();
32793         
32794         this.fireEvent('click', this);
32795     },
32796     
32797     onDownload : function(e)
32798     {
32799         e.preventDefault();
32800         
32801         this.fireEvent('download', this);
32802     },
32803     
32804     onTrash : function(e)
32805     {
32806         e.preventDefault();
32807         
32808         this.fireEvent('trash', this);
32809     }
32810     
32811 });
32812 /*
32813  * - LGPL
32814  *
32815  * nav progress bar
32816  * 
32817  */
32818
32819 /**
32820  * @class Roo.bootstrap.NavProgressBar
32821  * @extends Roo.bootstrap.Component
32822  * Bootstrap NavProgressBar class
32823  * 
32824  * @constructor
32825  * Create a new nav progress bar
32826  * @param {Object} config The config object
32827  */
32828
32829 Roo.bootstrap.NavProgressBar = function(config){
32830     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32831
32832     this.bullets = this.bullets || [];
32833    
32834 //    Roo.bootstrap.NavProgressBar.register(this);
32835      this.addEvents({
32836         /**
32837              * @event changed
32838              * Fires when the active item changes
32839              * @param {Roo.bootstrap.NavProgressBar} this
32840              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32841              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32842          */
32843         'changed': true
32844      });
32845     
32846 };
32847
32848 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32849     
32850     bullets : [],
32851     barItems : [],
32852     
32853     getAutoCreate : function()
32854     {
32855         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32856         
32857         cfg = {
32858             tag : 'div',
32859             cls : 'roo-navigation-bar-group',
32860             cn : [
32861                 {
32862                     tag : 'div',
32863                     cls : 'roo-navigation-top-bar'
32864                 },
32865                 {
32866                     tag : 'div',
32867                     cls : 'roo-navigation-bullets-bar',
32868                     cn : [
32869                         {
32870                             tag : 'ul',
32871                             cls : 'roo-navigation-bar'
32872                         }
32873                     ]
32874                 },
32875                 
32876                 {
32877                     tag : 'div',
32878                     cls : 'roo-navigation-bottom-bar'
32879                 }
32880             ]
32881             
32882         };
32883         
32884         return cfg;
32885         
32886     },
32887     
32888     initEvents: function() 
32889     {
32890         
32891     },
32892     
32893     onRender : function(ct, position) 
32894     {
32895         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32896         
32897         if(this.bullets.length){
32898             Roo.each(this.bullets, function(b){
32899                this.addItem(b);
32900             }, this);
32901         }
32902         
32903         this.format();
32904         
32905     },
32906     
32907     addItem : function(cfg)
32908     {
32909         var item = new Roo.bootstrap.NavProgressItem(cfg);
32910         
32911         item.parentId = this.id;
32912         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32913         
32914         if(cfg.html){
32915             var top = new Roo.bootstrap.Element({
32916                 tag : 'div',
32917                 cls : 'roo-navigation-bar-text'
32918             });
32919             
32920             var bottom = new Roo.bootstrap.Element({
32921                 tag : 'div',
32922                 cls : 'roo-navigation-bar-text'
32923             });
32924             
32925             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32926             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32927             
32928             var topText = new Roo.bootstrap.Element({
32929                 tag : 'span',
32930                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32931             });
32932             
32933             var bottomText = new Roo.bootstrap.Element({
32934                 tag : 'span',
32935                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32936             });
32937             
32938             topText.onRender(top.el, null);
32939             bottomText.onRender(bottom.el, null);
32940             
32941             item.topEl = top;
32942             item.bottomEl = bottom;
32943         }
32944         
32945         this.barItems.push(item);
32946         
32947         return item;
32948     },
32949     
32950     getActive : function()
32951     {
32952         var active = false;
32953         
32954         Roo.each(this.barItems, function(v){
32955             
32956             if (!v.isActive()) {
32957                 return;
32958             }
32959             
32960             active = v;
32961             return false;
32962             
32963         });
32964         
32965         return active;
32966     },
32967     
32968     setActiveItem : function(item)
32969     {
32970         var prev = false;
32971         
32972         Roo.each(this.barItems, function(v){
32973             if (v.rid == item.rid) {
32974                 return ;
32975             }
32976             
32977             if (v.isActive()) {
32978                 v.setActive(false);
32979                 prev = v;
32980             }
32981         });
32982
32983         item.setActive(true);
32984         
32985         this.fireEvent('changed', this, item, prev);
32986     },
32987     
32988     getBarItem: function(rid)
32989     {
32990         var ret = false;
32991         
32992         Roo.each(this.barItems, function(e) {
32993             if (e.rid != rid) {
32994                 return;
32995             }
32996             
32997             ret =  e;
32998             return false;
32999         });
33000         
33001         return ret;
33002     },
33003     
33004     indexOfItem : function(item)
33005     {
33006         var index = false;
33007         
33008         Roo.each(this.barItems, function(v, i){
33009             
33010             if (v.rid != item.rid) {
33011                 return;
33012             }
33013             
33014             index = i;
33015             return false
33016         });
33017         
33018         return index;
33019     },
33020     
33021     setActiveNext : function()
33022     {
33023         var i = this.indexOfItem(this.getActive());
33024         
33025         if (i > this.barItems.length) {
33026             return;
33027         }
33028         
33029         this.setActiveItem(this.barItems[i+1]);
33030     },
33031     
33032     setActivePrev : function()
33033     {
33034         var i = this.indexOfItem(this.getActive());
33035         
33036         if (i  < 1) {
33037             return;
33038         }
33039         
33040         this.setActiveItem(this.barItems[i-1]);
33041     },
33042     
33043     format : function()
33044     {
33045         if(!this.barItems.length){
33046             return;
33047         }
33048      
33049         var width = 100 / this.barItems.length;
33050         
33051         Roo.each(this.barItems, function(i){
33052             i.el.setStyle('width', width + '%');
33053             i.topEl.el.setStyle('width', width + '%');
33054             i.bottomEl.el.setStyle('width', width + '%');
33055         }, this);
33056         
33057     }
33058     
33059 });
33060 /*
33061  * - LGPL
33062  *
33063  * Nav Progress Item
33064  * 
33065  */
33066
33067 /**
33068  * @class Roo.bootstrap.NavProgressItem
33069  * @extends Roo.bootstrap.Component
33070  * Bootstrap NavProgressItem class
33071  * @cfg {String} rid the reference id
33072  * @cfg {Boolean} active (true|false) Is item active default false
33073  * @cfg {Boolean} disabled (true|false) Is item active default false
33074  * @cfg {String} html
33075  * @cfg {String} position (top|bottom) text position default bottom
33076  * @cfg {String} icon show icon instead of number
33077  * 
33078  * @constructor
33079  * Create a new NavProgressItem
33080  * @param {Object} config The config object
33081  */
33082 Roo.bootstrap.NavProgressItem = function(config){
33083     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33084     this.addEvents({
33085         // raw events
33086         /**
33087          * @event click
33088          * The raw click event for the entire grid.
33089          * @param {Roo.bootstrap.NavProgressItem} this
33090          * @param {Roo.EventObject} e
33091          */
33092         "click" : true
33093     });
33094    
33095 };
33096
33097 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33098     
33099     rid : '',
33100     active : false,
33101     disabled : false,
33102     html : '',
33103     position : 'bottom',
33104     icon : false,
33105     
33106     getAutoCreate : function()
33107     {
33108         var iconCls = 'roo-navigation-bar-item-icon';
33109         
33110         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33111         
33112         var cfg = {
33113             tag: 'li',
33114             cls: 'roo-navigation-bar-item',
33115             cn : [
33116                 {
33117                     tag : 'i',
33118                     cls : iconCls
33119                 }
33120             ]
33121         };
33122         
33123         if(this.active){
33124             cfg.cls += ' active';
33125         }
33126         if(this.disabled){
33127             cfg.cls += ' disabled';
33128         }
33129         
33130         return cfg;
33131     },
33132     
33133     disable : function()
33134     {
33135         this.setDisabled(true);
33136     },
33137     
33138     enable : function()
33139     {
33140         this.setDisabled(false);
33141     },
33142     
33143     initEvents: function() 
33144     {
33145         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33146         
33147         this.iconEl.on('click', this.onClick, this);
33148     },
33149     
33150     onClick : function(e)
33151     {
33152         e.preventDefault();
33153         
33154         if(this.disabled){
33155             return;
33156         }
33157         
33158         if(this.fireEvent('click', this, e) === false){
33159             return;
33160         };
33161         
33162         this.parent().setActiveItem(this);
33163     },
33164     
33165     isActive: function () 
33166     {
33167         return this.active;
33168     },
33169     
33170     setActive : function(state)
33171     {
33172         if(this.active == state){
33173             return;
33174         }
33175         
33176         this.active = state;
33177         
33178         if (state) {
33179             this.el.addClass('active');
33180             return;
33181         }
33182         
33183         this.el.removeClass('active');
33184         
33185         return;
33186     },
33187     
33188     setDisabled : function(state)
33189     {
33190         if(this.disabled == state){
33191             return;
33192         }
33193         
33194         this.disabled = state;
33195         
33196         if (state) {
33197             this.el.addClass('disabled');
33198             return;
33199         }
33200         
33201         this.el.removeClass('disabled');
33202     },
33203     
33204     tooltipEl : function()
33205     {
33206         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33207     }
33208 });
33209  
33210
33211  /*
33212  * - LGPL
33213  *
33214  * FieldLabel
33215  * 
33216  */
33217
33218 /**
33219  * @class Roo.bootstrap.FieldLabel
33220  * @extends Roo.bootstrap.Component
33221  * Bootstrap FieldLabel class
33222  * @cfg {String} html contents of the element
33223  * @cfg {String} tag tag of the element default label
33224  * @cfg {String} cls class of the element
33225  * @cfg {String} target label target 
33226  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33227  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33228  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33229  * @cfg {String} iconTooltip default "This field is required"
33230  * @cfg {String} indicatorpos (left|right) default left
33231  * 
33232  * @constructor
33233  * Create a new FieldLabel
33234  * @param {Object} config The config object
33235  */
33236
33237 Roo.bootstrap.FieldLabel = function(config){
33238     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33239     
33240     this.addEvents({
33241             /**
33242              * @event invalid
33243              * Fires after the field has been marked as invalid.
33244              * @param {Roo.form.FieldLabel} this
33245              * @param {String} msg The validation message
33246              */
33247             invalid : true,
33248             /**
33249              * @event valid
33250              * Fires after the field has been validated with no errors.
33251              * @param {Roo.form.FieldLabel} this
33252              */
33253             valid : true
33254         });
33255 };
33256
33257 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33258     
33259     tag: 'label',
33260     cls: '',
33261     html: '',
33262     target: '',
33263     allowBlank : true,
33264     invalidClass : 'has-warning',
33265     validClass : 'has-success',
33266     iconTooltip : 'This field is required',
33267     indicatorpos : 'left',
33268     
33269     getAutoCreate : function(){
33270         
33271         var cls = "";
33272         if (!this.allowBlank) {
33273             cls  = "visible";
33274         }
33275         
33276         var cfg = {
33277             tag : this.tag,
33278             cls : 'roo-bootstrap-field-label ' + this.cls,
33279             for : this.target,
33280             cn : [
33281                 {
33282                     tag : 'i',
33283                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33284                     tooltip : this.iconTooltip
33285                 },
33286                 {
33287                     tag : 'span',
33288                     html : this.html
33289                 }
33290             ] 
33291         };
33292         
33293         if(this.indicatorpos == 'right'){
33294             var cfg = {
33295                 tag : this.tag,
33296                 cls : 'roo-bootstrap-field-label ' + this.cls,
33297                 for : this.target,
33298                 cn : [
33299                     {
33300                         tag : 'span',
33301                         html : this.html
33302                     },
33303                     {
33304                         tag : 'i',
33305                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33306                         tooltip : this.iconTooltip
33307                     }
33308                 ] 
33309             };
33310         }
33311         
33312         return cfg;
33313     },
33314     
33315     initEvents: function() 
33316     {
33317         Roo.bootstrap.Element.superclass.initEvents.call(this);
33318         
33319         this.indicator = this.indicatorEl();
33320         
33321         if(this.indicator){
33322             this.indicator.removeClass('visible');
33323             this.indicator.addClass('invisible');
33324         }
33325         
33326         Roo.bootstrap.FieldLabel.register(this);
33327     },
33328     
33329     indicatorEl : function()
33330     {
33331         var indicator = this.el.select('i.roo-required-indicator',true).first();
33332         
33333         if(!indicator){
33334             return false;
33335         }
33336         
33337         return indicator;
33338         
33339     },
33340     
33341     /**
33342      * Mark this field as valid
33343      */
33344     markValid : function()
33345     {
33346         if(this.indicator){
33347             this.indicator.removeClass('visible');
33348             this.indicator.addClass('invisible');
33349         }
33350         if (Roo.bootstrap.version == 3) {
33351             this.el.removeClass(this.invalidClass);
33352             this.el.addClass(this.validClass);
33353         } else {
33354             this.el.removeClass('is-invalid');
33355             this.el.addClass('is-valid');
33356         }
33357         
33358         
33359         this.fireEvent('valid', this);
33360     },
33361     
33362     /**
33363      * Mark this field as invalid
33364      * @param {String} msg The validation message
33365      */
33366     markInvalid : function(msg)
33367     {
33368         if(this.indicator){
33369             this.indicator.removeClass('invisible');
33370             this.indicator.addClass('visible');
33371         }
33372           if (Roo.bootstrap.version == 3) {
33373             this.el.removeClass(this.validClass);
33374             this.el.addClass(this.invalidClass);
33375         } else {
33376             this.el.removeClass('is-valid');
33377             this.el.addClass('is-invalid');
33378         }
33379         
33380         
33381         this.fireEvent('invalid', this, msg);
33382     }
33383     
33384    
33385 });
33386
33387 Roo.apply(Roo.bootstrap.FieldLabel, {
33388     
33389     groups: {},
33390     
33391      /**
33392     * register a FieldLabel Group
33393     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33394     */
33395     register : function(label)
33396     {
33397         if(this.groups.hasOwnProperty(label.target)){
33398             return;
33399         }
33400      
33401         this.groups[label.target] = label;
33402         
33403     },
33404     /**
33405     * fetch a FieldLabel Group based on the target
33406     * @param {string} target
33407     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33408     */
33409     get: function(target) {
33410         if (typeof(this.groups[target]) == 'undefined') {
33411             return false;
33412         }
33413         
33414         return this.groups[target] ;
33415     }
33416 });
33417
33418  
33419
33420  /*
33421  * - LGPL
33422  *
33423  * page DateSplitField.
33424  * 
33425  */
33426
33427
33428 /**
33429  * @class Roo.bootstrap.DateSplitField
33430  * @extends Roo.bootstrap.Component
33431  * Bootstrap DateSplitField class
33432  * @cfg {string} fieldLabel - the label associated
33433  * @cfg {Number} labelWidth set the width of label (0-12)
33434  * @cfg {String} labelAlign (top|left)
33435  * @cfg {Boolean} dayAllowBlank (true|false) default false
33436  * @cfg {Boolean} monthAllowBlank (true|false) default false
33437  * @cfg {Boolean} yearAllowBlank (true|false) default false
33438  * @cfg {string} dayPlaceholder 
33439  * @cfg {string} monthPlaceholder
33440  * @cfg {string} yearPlaceholder
33441  * @cfg {string} dayFormat default 'd'
33442  * @cfg {string} monthFormat default 'm'
33443  * @cfg {string} yearFormat default 'Y'
33444  * @cfg {Number} labellg set the width of label (1-12)
33445  * @cfg {Number} labelmd set the width of label (1-12)
33446  * @cfg {Number} labelsm set the width of label (1-12)
33447  * @cfg {Number} labelxs set the width of label (1-12)
33448
33449  *     
33450  * @constructor
33451  * Create a new DateSplitField
33452  * @param {Object} config The config object
33453  */
33454
33455 Roo.bootstrap.DateSplitField = function(config){
33456     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33457     
33458     this.addEvents({
33459         // raw events
33460          /**
33461          * @event years
33462          * getting the data of years
33463          * @param {Roo.bootstrap.DateSplitField} this
33464          * @param {Object} years
33465          */
33466         "years" : true,
33467         /**
33468          * @event days
33469          * getting the data of days
33470          * @param {Roo.bootstrap.DateSplitField} this
33471          * @param {Object} days
33472          */
33473         "days" : true,
33474         /**
33475          * @event invalid
33476          * Fires after the field has been marked as invalid.
33477          * @param {Roo.form.Field} this
33478          * @param {String} msg The validation message
33479          */
33480         invalid : true,
33481        /**
33482          * @event valid
33483          * Fires after the field has been validated with no errors.
33484          * @param {Roo.form.Field} this
33485          */
33486         valid : true
33487     });
33488 };
33489
33490 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33491     
33492     fieldLabel : '',
33493     labelAlign : 'top',
33494     labelWidth : 3,
33495     dayAllowBlank : false,
33496     monthAllowBlank : false,
33497     yearAllowBlank : false,
33498     dayPlaceholder : '',
33499     monthPlaceholder : '',
33500     yearPlaceholder : '',
33501     dayFormat : 'd',
33502     monthFormat : 'm',
33503     yearFormat : 'Y',
33504     isFormField : true,
33505     labellg : 0,
33506     labelmd : 0,
33507     labelsm : 0,
33508     labelxs : 0,
33509     
33510     getAutoCreate : function()
33511     {
33512         var cfg = {
33513             tag : 'div',
33514             cls : 'row roo-date-split-field-group',
33515             cn : [
33516                 {
33517                     tag : 'input',
33518                     type : 'hidden',
33519                     cls : 'form-hidden-field roo-date-split-field-group-value',
33520                     name : this.name
33521                 }
33522             ]
33523         };
33524         
33525         var labelCls = 'col-md-12';
33526         var contentCls = 'col-md-4';
33527         
33528         if(this.fieldLabel){
33529             
33530             var label = {
33531                 tag : 'div',
33532                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33533                 cn : [
33534                     {
33535                         tag : 'label',
33536                         html : this.fieldLabel
33537                     }
33538                 ]
33539             };
33540             
33541             if(this.labelAlign == 'left'){
33542             
33543                 if(this.labelWidth > 12){
33544                     label.style = "width: " + this.labelWidth + 'px';
33545                 }
33546
33547                 if(this.labelWidth < 13 && this.labelmd == 0){
33548                     this.labelmd = this.labelWidth;
33549                 }
33550
33551                 if(this.labellg > 0){
33552                     labelCls = ' col-lg-' + this.labellg;
33553                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33554                 }
33555
33556                 if(this.labelmd > 0){
33557                     labelCls = ' col-md-' + this.labelmd;
33558                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33559                 }
33560
33561                 if(this.labelsm > 0){
33562                     labelCls = ' col-sm-' + this.labelsm;
33563                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33564                 }
33565
33566                 if(this.labelxs > 0){
33567                     labelCls = ' col-xs-' + this.labelxs;
33568                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33569                 }
33570             }
33571             
33572             label.cls += ' ' + labelCls;
33573             
33574             cfg.cn.push(label);
33575         }
33576         
33577         Roo.each(['day', 'month', 'year'], function(t){
33578             cfg.cn.push({
33579                 tag : 'div',
33580                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33581             });
33582         }, this);
33583         
33584         return cfg;
33585     },
33586     
33587     inputEl: function ()
33588     {
33589         return this.el.select('.roo-date-split-field-group-value', true).first();
33590     },
33591     
33592     onRender : function(ct, position) 
33593     {
33594         var _this = this;
33595         
33596         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33597         
33598         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33599         
33600         this.dayField = new Roo.bootstrap.ComboBox({
33601             allowBlank : this.dayAllowBlank,
33602             alwaysQuery : true,
33603             displayField : 'value',
33604             editable : false,
33605             fieldLabel : '',
33606             forceSelection : true,
33607             mode : 'local',
33608             placeholder : this.dayPlaceholder,
33609             selectOnFocus : true,
33610             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33611             triggerAction : 'all',
33612             typeAhead : true,
33613             valueField : 'value',
33614             store : new Roo.data.SimpleStore({
33615                 data : (function() {    
33616                     var days = [];
33617                     _this.fireEvent('days', _this, days);
33618                     return days;
33619                 })(),
33620                 fields : [ 'value' ]
33621             }),
33622             listeners : {
33623                 select : function (_self, record, index)
33624                 {
33625                     _this.setValue(_this.getValue());
33626                 }
33627             }
33628         });
33629
33630         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33631         
33632         this.monthField = new Roo.bootstrap.MonthField({
33633             after : '<i class=\"fa fa-calendar\"></i>',
33634             allowBlank : this.monthAllowBlank,
33635             placeholder : this.monthPlaceholder,
33636             readOnly : true,
33637             listeners : {
33638                 render : function (_self)
33639                 {
33640                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33641                         e.preventDefault();
33642                         _self.focus();
33643                     });
33644                 },
33645                 select : function (_self, oldvalue, newvalue)
33646                 {
33647                     _this.setValue(_this.getValue());
33648                 }
33649             }
33650         });
33651         
33652         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33653         
33654         this.yearField = new Roo.bootstrap.ComboBox({
33655             allowBlank : this.yearAllowBlank,
33656             alwaysQuery : true,
33657             displayField : 'value',
33658             editable : false,
33659             fieldLabel : '',
33660             forceSelection : true,
33661             mode : 'local',
33662             placeholder : this.yearPlaceholder,
33663             selectOnFocus : true,
33664             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33665             triggerAction : 'all',
33666             typeAhead : true,
33667             valueField : 'value',
33668             store : new Roo.data.SimpleStore({
33669                 data : (function() {
33670                     var years = [];
33671                     _this.fireEvent('years', _this, years);
33672                     return years;
33673                 })(),
33674                 fields : [ 'value' ]
33675             }),
33676             listeners : {
33677                 select : function (_self, record, index)
33678                 {
33679                     _this.setValue(_this.getValue());
33680                 }
33681             }
33682         });
33683
33684         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33685     },
33686     
33687     setValue : function(v, format)
33688     {
33689         this.inputEl.dom.value = v;
33690         
33691         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33692         
33693         var d = Date.parseDate(v, f);
33694         
33695         if(!d){
33696             this.validate();
33697             return;
33698         }
33699         
33700         this.setDay(d.format(this.dayFormat));
33701         this.setMonth(d.format(this.monthFormat));
33702         this.setYear(d.format(this.yearFormat));
33703         
33704         this.validate();
33705         
33706         return;
33707     },
33708     
33709     setDay : function(v)
33710     {
33711         this.dayField.setValue(v);
33712         this.inputEl.dom.value = this.getValue();
33713         this.validate();
33714         return;
33715     },
33716     
33717     setMonth : function(v)
33718     {
33719         this.monthField.setValue(v, true);
33720         this.inputEl.dom.value = this.getValue();
33721         this.validate();
33722         return;
33723     },
33724     
33725     setYear : function(v)
33726     {
33727         this.yearField.setValue(v);
33728         this.inputEl.dom.value = this.getValue();
33729         this.validate();
33730         return;
33731     },
33732     
33733     getDay : function()
33734     {
33735         return this.dayField.getValue();
33736     },
33737     
33738     getMonth : function()
33739     {
33740         return this.monthField.getValue();
33741     },
33742     
33743     getYear : function()
33744     {
33745         return this.yearField.getValue();
33746     },
33747     
33748     getValue : function()
33749     {
33750         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33751         
33752         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33753         
33754         return date;
33755     },
33756     
33757     reset : function()
33758     {
33759         this.setDay('');
33760         this.setMonth('');
33761         this.setYear('');
33762         this.inputEl.dom.value = '';
33763         this.validate();
33764         return;
33765     },
33766     
33767     validate : function()
33768     {
33769         var d = this.dayField.validate();
33770         var m = this.monthField.validate();
33771         var y = this.yearField.validate();
33772         
33773         var valid = true;
33774         
33775         if(
33776                 (!this.dayAllowBlank && !d) ||
33777                 (!this.monthAllowBlank && !m) ||
33778                 (!this.yearAllowBlank && !y)
33779         ){
33780             valid = false;
33781         }
33782         
33783         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33784             return valid;
33785         }
33786         
33787         if(valid){
33788             this.markValid();
33789             return valid;
33790         }
33791         
33792         this.markInvalid();
33793         
33794         return valid;
33795     },
33796     
33797     markValid : function()
33798     {
33799         
33800         var label = this.el.select('label', true).first();
33801         var icon = this.el.select('i.fa-star', true).first();
33802
33803         if(label && icon){
33804             icon.remove();
33805         }
33806         
33807         this.fireEvent('valid', this);
33808     },
33809     
33810      /**
33811      * Mark this field as invalid
33812      * @param {String} msg The validation message
33813      */
33814     markInvalid : function(msg)
33815     {
33816         
33817         var label = this.el.select('label', true).first();
33818         var icon = this.el.select('i.fa-star', true).first();
33819
33820         if(label && !icon){
33821             this.el.select('.roo-date-split-field-label', true).createChild({
33822                 tag : 'i',
33823                 cls : 'text-danger fa fa-lg fa-star',
33824                 tooltip : 'This field is required',
33825                 style : 'margin-right:5px;'
33826             }, label, true);
33827         }
33828         
33829         this.fireEvent('invalid', this, msg);
33830     },
33831     
33832     clearInvalid : function()
33833     {
33834         var label = this.el.select('label', true).first();
33835         var icon = this.el.select('i.fa-star', true).first();
33836
33837         if(label && icon){
33838             icon.remove();
33839         }
33840         
33841         this.fireEvent('valid', this);
33842     },
33843     
33844     getName: function()
33845     {
33846         return this.name;
33847     }
33848     
33849 });
33850
33851  /**
33852  *
33853  * This is based on 
33854  * http://masonry.desandro.com
33855  *
33856  * The idea is to render all the bricks based on vertical width...
33857  *
33858  * The original code extends 'outlayer' - we might need to use that....
33859  * 
33860  */
33861
33862
33863 /**
33864  * @class Roo.bootstrap.LayoutMasonry
33865  * @extends Roo.bootstrap.Component
33866  * Bootstrap Layout Masonry class
33867  * 
33868  * @constructor
33869  * Create a new Element
33870  * @param {Object} config The config object
33871  */
33872
33873 Roo.bootstrap.LayoutMasonry = function(config){
33874     
33875     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33876     
33877     this.bricks = [];
33878     
33879     Roo.bootstrap.LayoutMasonry.register(this);
33880     
33881     this.addEvents({
33882         // raw events
33883         /**
33884          * @event layout
33885          * Fire after layout the items
33886          * @param {Roo.bootstrap.LayoutMasonry} this
33887          * @param {Roo.EventObject} e
33888          */
33889         "layout" : true
33890     });
33891     
33892 };
33893
33894 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33895     
33896     /**
33897      * @cfg {Boolean} isLayoutInstant = no animation?
33898      */   
33899     isLayoutInstant : false, // needed?
33900    
33901     /**
33902      * @cfg {Number} boxWidth  width of the columns
33903      */   
33904     boxWidth : 450,
33905     
33906       /**
33907      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33908      */   
33909     boxHeight : 0,
33910     
33911     /**
33912      * @cfg {Number} padWidth padding below box..
33913      */   
33914     padWidth : 10, 
33915     
33916     /**
33917      * @cfg {Number} gutter gutter width..
33918      */   
33919     gutter : 10,
33920     
33921      /**
33922      * @cfg {Number} maxCols maximum number of columns
33923      */   
33924     
33925     maxCols: 0,
33926     
33927     /**
33928      * @cfg {Boolean} isAutoInitial defalut true
33929      */   
33930     isAutoInitial : true, 
33931     
33932     containerWidth: 0,
33933     
33934     /**
33935      * @cfg {Boolean} isHorizontal defalut false
33936      */   
33937     isHorizontal : false, 
33938
33939     currentSize : null,
33940     
33941     tag: 'div',
33942     
33943     cls: '',
33944     
33945     bricks: null, //CompositeElement
33946     
33947     cols : 1,
33948     
33949     _isLayoutInited : false,
33950     
33951 //    isAlternative : false, // only use for vertical layout...
33952     
33953     /**
33954      * @cfg {Number} alternativePadWidth padding below box..
33955      */   
33956     alternativePadWidth : 50,
33957     
33958     selectedBrick : [],
33959     
33960     getAutoCreate : function(){
33961         
33962         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33963         
33964         var cfg = {
33965             tag: this.tag,
33966             cls: 'blog-masonary-wrapper ' + this.cls,
33967             cn : {
33968                 cls : 'mas-boxes masonary'
33969             }
33970         };
33971         
33972         return cfg;
33973     },
33974     
33975     getChildContainer: function( )
33976     {
33977         if (this.boxesEl) {
33978             return this.boxesEl;
33979         }
33980         
33981         this.boxesEl = this.el.select('.mas-boxes').first();
33982         
33983         return this.boxesEl;
33984     },
33985     
33986     
33987     initEvents : function()
33988     {
33989         var _this = this;
33990         
33991         if(this.isAutoInitial){
33992             Roo.log('hook children rendered');
33993             this.on('childrenrendered', function() {
33994                 Roo.log('children rendered');
33995                 _this.initial();
33996             } ,this);
33997         }
33998     },
33999     
34000     initial : function()
34001     {
34002         this.selectedBrick = [];
34003         
34004         this.currentSize = this.el.getBox(true);
34005         
34006         Roo.EventManager.onWindowResize(this.resize, this); 
34007
34008         if(!this.isAutoInitial){
34009             this.layout();
34010             return;
34011         }
34012         
34013         this.layout();
34014         
34015         return;
34016         //this.layout.defer(500,this);
34017         
34018     },
34019     
34020     resize : function()
34021     {
34022         var cs = this.el.getBox(true);
34023         
34024         if (
34025                 this.currentSize.width == cs.width && 
34026                 this.currentSize.x == cs.x && 
34027                 this.currentSize.height == cs.height && 
34028                 this.currentSize.y == cs.y 
34029         ) {
34030             Roo.log("no change in with or X or Y");
34031             return;
34032         }
34033         
34034         this.currentSize = cs;
34035         
34036         this.layout();
34037         
34038     },
34039     
34040     layout : function()
34041     {   
34042         this._resetLayout();
34043         
34044         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34045         
34046         this.layoutItems( isInstant );
34047       
34048         this._isLayoutInited = true;
34049         
34050         this.fireEvent('layout', this);
34051         
34052     },
34053     
34054     _resetLayout : function()
34055     {
34056         if(this.isHorizontal){
34057             this.horizontalMeasureColumns();
34058             return;
34059         }
34060         
34061         this.verticalMeasureColumns();
34062         
34063     },
34064     
34065     verticalMeasureColumns : function()
34066     {
34067         this.getContainerWidth();
34068         
34069 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34070 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34071 //            return;
34072 //        }
34073         
34074         var boxWidth = this.boxWidth + this.padWidth;
34075         
34076         if(this.containerWidth < this.boxWidth){
34077             boxWidth = this.containerWidth
34078         }
34079         
34080         var containerWidth = this.containerWidth;
34081         
34082         var cols = Math.floor(containerWidth / boxWidth);
34083         
34084         this.cols = Math.max( cols, 1 );
34085         
34086         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34087         
34088         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34089         
34090         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34091         
34092         this.colWidth = boxWidth + avail - this.padWidth;
34093         
34094         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34095         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34096     },
34097     
34098     horizontalMeasureColumns : function()
34099     {
34100         this.getContainerWidth();
34101         
34102         var boxWidth = this.boxWidth;
34103         
34104         if(this.containerWidth < boxWidth){
34105             boxWidth = this.containerWidth;
34106         }
34107         
34108         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34109         
34110         this.el.setHeight(boxWidth);
34111         
34112     },
34113     
34114     getContainerWidth : function()
34115     {
34116         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34117     },
34118     
34119     layoutItems : function( isInstant )
34120     {
34121         Roo.log(this.bricks);
34122         
34123         var items = Roo.apply([], this.bricks);
34124         
34125         if(this.isHorizontal){
34126             this._horizontalLayoutItems( items , isInstant );
34127             return;
34128         }
34129         
34130 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34131 //            this._verticalAlternativeLayoutItems( items , isInstant );
34132 //            return;
34133 //        }
34134         
34135         this._verticalLayoutItems( items , isInstant );
34136         
34137     },
34138     
34139     _verticalLayoutItems : function ( items , isInstant)
34140     {
34141         if ( !items || !items.length ) {
34142             return;
34143         }
34144         
34145         var standard = [
34146             ['xs', 'xs', 'xs', 'tall'],
34147             ['xs', 'xs', 'tall'],
34148             ['xs', 'xs', 'sm'],
34149             ['xs', 'xs', 'xs'],
34150             ['xs', 'tall'],
34151             ['xs', 'sm'],
34152             ['xs', 'xs'],
34153             ['xs'],
34154             
34155             ['sm', 'xs', 'xs'],
34156             ['sm', 'xs'],
34157             ['sm'],
34158             
34159             ['tall', 'xs', 'xs', 'xs'],
34160             ['tall', 'xs', 'xs'],
34161             ['tall', 'xs'],
34162             ['tall']
34163             
34164         ];
34165         
34166         var queue = [];
34167         
34168         var boxes = [];
34169         
34170         var box = [];
34171         
34172         Roo.each(items, function(item, k){
34173             
34174             switch (item.size) {
34175                 // these layouts take up a full box,
34176                 case 'md' :
34177                 case 'md-left' :
34178                 case 'md-right' :
34179                 case 'wide' :
34180                     
34181                     if(box.length){
34182                         boxes.push(box);
34183                         box = [];
34184                     }
34185                     
34186                     boxes.push([item]);
34187                     
34188                     break;
34189                     
34190                 case 'xs' :
34191                 case 'sm' :
34192                 case 'tall' :
34193                     
34194                     box.push(item);
34195                     
34196                     break;
34197                 default :
34198                     break;
34199                     
34200             }
34201             
34202         }, this);
34203         
34204         if(box.length){
34205             boxes.push(box);
34206             box = [];
34207         }
34208         
34209         var filterPattern = function(box, length)
34210         {
34211             if(!box.length){
34212                 return;
34213             }
34214             
34215             var match = false;
34216             
34217             var pattern = box.slice(0, length);
34218             
34219             var format = [];
34220             
34221             Roo.each(pattern, function(i){
34222                 format.push(i.size);
34223             }, this);
34224             
34225             Roo.each(standard, function(s){
34226                 
34227                 if(String(s) != String(format)){
34228                     return;
34229                 }
34230                 
34231                 match = true;
34232                 return false;
34233                 
34234             }, this);
34235             
34236             if(!match && length == 1){
34237                 return;
34238             }
34239             
34240             if(!match){
34241                 filterPattern(box, length - 1);
34242                 return;
34243             }
34244                 
34245             queue.push(pattern);
34246
34247             box = box.slice(length, box.length);
34248
34249             filterPattern(box, 4);
34250
34251             return;
34252             
34253         }
34254         
34255         Roo.each(boxes, function(box, k){
34256             
34257             if(!box.length){
34258                 return;
34259             }
34260             
34261             if(box.length == 1){
34262                 queue.push(box);
34263                 return;
34264             }
34265             
34266             filterPattern(box, 4);
34267             
34268         }, this);
34269         
34270         this._processVerticalLayoutQueue( queue, isInstant );
34271         
34272     },
34273     
34274 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34275 //    {
34276 //        if ( !items || !items.length ) {
34277 //            return;
34278 //        }
34279 //
34280 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34281 //        
34282 //    },
34283     
34284     _horizontalLayoutItems : function ( items , isInstant)
34285     {
34286         if ( !items || !items.length || items.length < 3) {
34287             return;
34288         }
34289         
34290         items.reverse();
34291         
34292         var eItems = items.slice(0, 3);
34293         
34294         items = items.slice(3, items.length);
34295         
34296         var standard = [
34297             ['xs', 'xs', 'xs', 'wide'],
34298             ['xs', 'xs', 'wide'],
34299             ['xs', 'xs', 'sm'],
34300             ['xs', 'xs', 'xs'],
34301             ['xs', 'wide'],
34302             ['xs', 'sm'],
34303             ['xs', 'xs'],
34304             ['xs'],
34305             
34306             ['sm', 'xs', 'xs'],
34307             ['sm', 'xs'],
34308             ['sm'],
34309             
34310             ['wide', 'xs', 'xs', 'xs'],
34311             ['wide', 'xs', 'xs'],
34312             ['wide', 'xs'],
34313             ['wide'],
34314             
34315             ['wide-thin']
34316         ];
34317         
34318         var queue = [];
34319         
34320         var boxes = [];
34321         
34322         var box = [];
34323         
34324         Roo.each(items, function(item, k){
34325             
34326             switch (item.size) {
34327                 case 'md' :
34328                 case 'md-left' :
34329                 case 'md-right' :
34330                 case 'tall' :
34331                     
34332                     if(box.length){
34333                         boxes.push(box);
34334                         box = [];
34335                     }
34336                     
34337                     boxes.push([item]);
34338                     
34339                     break;
34340                     
34341                 case 'xs' :
34342                 case 'sm' :
34343                 case 'wide' :
34344                 case 'wide-thin' :
34345                     
34346                     box.push(item);
34347                     
34348                     break;
34349                 default :
34350                     break;
34351                     
34352             }
34353             
34354         }, this);
34355         
34356         if(box.length){
34357             boxes.push(box);
34358             box = [];
34359         }
34360         
34361         var filterPattern = function(box, length)
34362         {
34363             if(!box.length){
34364                 return;
34365             }
34366             
34367             var match = false;
34368             
34369             var pattern = box.slice(0, length);
34370             
34371             var format = [];
34372             
34373             Roo.each(pattern, function(i){
34374                 format.push(i.size);
34375             }, this);
34376             
34377             Roo.each(standard, function(s){
34378                 
34379                 if(String(s) != String(format)){
34380                     return;
34381                 }
34382                 
34383                 match = true;
34384                 return false;
34385                 
34386             }, this);
34387             
34388             if(!match && length == 1){
34389                 return;
34390             }
34391             
34392             if(!match){
34393                 filterPattern(box, length - 1);
34394                 return;
34395             }
34396                 
34397             queue.push(pattern);
34398
34399             box = box.slice(length, box.length);
34400
34401             filterPattern(box, 4);
34402
34403             return;
34404             
34405         }
34406         
34407         Roo.each(boxes, function(box, k){
34408             
34409             if(!box.length){
34410                 return;
34411             }
34412             
34413             if(box.length == 1){
34414                 queue.push(box);
34415                 return;
34416             }
34417             
34418             filterPattern(box, 4);
34419             
34420         }, this);
34421         
34422         
34423         var prune = [];
34424         
34425         var pos = this.el.getBox(true);
34426         
34427         var minX = pos.x;
34428         
34429         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34430         
34431         var hit_end = false;
34432         
34433         Roo.each(queue, function(box){
34434             
34435             if(hit_end){
34436                 
34437                 Roo.each(box, function(b){
34438                 
34439                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34440                     b.el.hide();
34441
34442                 }, this);
34443
34444                 return;
34445             }
34446             
34447             var mx = 0;
34448             
34449             Roo.each(box, function(b){
34450                 
34451                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34452                 b.el.show();
34453
34454                 mx = Math.max(mx, b.x);
34455                 
34456             }, this);
34457             
34458             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34459             
34460             if(maxX < minX){
34461                 
34462                 Roo.each(box, function(b){
34463                 
34464                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34465                     b.el.hide();
34466                     
34467                 }, this);
34468                 
34469                 hit_end = true;
34470                 
34471                 return;
34472             }
34473             
34474             prune.push(box);
34475             
34476         }, this);
34477         
34478         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34479     },
34480     
34481     /** Sets position of item in DOM
34482     * @param {Element} item
34483     * @param {Number} x - horizontal position
34484     * @param {Number} y - vertical position
34485     * @param {Boolean} isInstant - disables transitions
34486     */
34487     _processVerticalLayoutQueue : function( queue, isInstant )
34488     {
34489         var pos = this.el.getBox(true);
34490         var x = pos.x;
34491         var y = pos.y;
34492         var maxY = [];
34493         
34494         for (var i = 0; i < this.cols; i++){
34495             maxY[i] = pos.y;
34496         }
34497         
34498         Roo.each(queue, function(box, k){
34499             
34500             var col = k % this.cols;
34501             
34502             Roo.each(box, function(b,kk){
34503                 
34504                 b.el.position('absolute');
34505                 
34506                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34507                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34508                 
34509                 if(b.size == 'md-left' || b.size == 'md-right'){
34510                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34511                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34512                 }
34513                 
34514                 b.el.setWidth(width);
34515                 b.el.setHeight(height);
34516                 // iframe?
34517                 b.el.select('iframe',true).setSize(width,height);
34518                 
34519             }, this);
34520             
34521             for (var i = 0; i < this.cols; i++){
34522                 
34523                 if(maxY[i] < maxY[col]){
34524                     col = i;
34525                     continue;
34526                 }
34527                 
34528                 col = Math.min(col, i);
34529                 
34530             }
34531             
34532             x = pos.x + col * (this.colWidth + this.padWidth);
34533             
34534             y = maxY[col];
34535             
34536             var positions = [];
34537             
34538             switch (box.length){
34539                 case 1 :
34540                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34541                     break;
34542                 case 2 :
34543                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34544                     break;
34545                 case 3 :
34546                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34547                     break;
34548                 case 4 :
34549                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34550                     break;
34551                 default :
34552                     break;
34553             }
34554             
34555             Roo.each(box, function(b,kk){
34556                 
34557                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34558                 
34559                 var sz = b.el.getSize();
34560                 
34561                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34562                 
34563             }, this);
34564             
34565         }, this);
34566         
34567         var mY = 0;
34568         
34569         for (var i = 0; i < this.cols; i++){
34570             mY = Math.max(mY, maxY[i]);
34571         }
34572         
34573         this.el.setHeight(mY - pos.y);
34574         
34575     },
34576     
34577 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34578 //    {
34579 //        var pos = this.el.getBox(true);
34580 //        var x = pos.x;
34581 //        var y = pos.y;
34582 //        var maxX = pos.right;
34583 //        
34584 //        var maxHeight = 0;
34585 //        
34586 //        Roo.each(items, function(item, k){
34587 //            
34588 //            var c = k % 2;
34589 //            
34590 //            item.el.position('absolute');
34591 //                
34592 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34593 //
34594 //            item.el.setWidth(width);
34595 //
34596 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34597 //
34598 //            item.el.setHeight(height);
34599 //            
34600 //            if(c == 0){
34601 //                item.el.setXY([x, y], isInstant ? false : true);
34602 //            } else {
34603 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34604 //            }
34605 //            
34606 //            y = y + height + this.alternativePadWidth;
34607 //            
34608 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34609 //            
34610 //        }, this);
34611 //        
34612 //        this.el.setHeight(maxHeight);
34613 //        
34614 //    },
34615     
34616     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34617     {
34618         var pos = this.el.getBox(true);
34619         
34620         var minX = pos.x;
34621         var minY = pos.y;
34622         
34623         var maxX = pos.right;
34624         
34625         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34626         
34627         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34628         
34629         Roo.each(queue, function(box, k){
34630             
34631             Roo.each(box, function(b, kk){
34632                 
34633                 b.el.position('absolute');
34634                 
34635                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34636                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34637                 
34638                 if(b.size == 'md-left' || b.size == 'md-right'){
34639                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34640                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34641                 }
34642                 
34643                 b.el.setWidth(width);
34644                 b.el.setHeight(height);
34645                 
34646             }, this);
34647             
34648             if(!box.length){
34649                 return;
34650             }
34651             
34652             var positions = [];
34653             
34654             switch (box.length){
34655                 case 1 :
34656                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34657                     break;
34658                 case 2 :
34659                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34660                     break;
34661                 case 3 :
34662                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34663                     break;
34664                 case 4 :
34665                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34666                     break;
34667                 default :
34668                     break;
34669             }
34670             
34671             Roo.each(box, function(b,kk){
34672                 
34673                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34674                 
34675                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34676                 
34677             }, this);
34678             
34679         }, this);
34680         
34681     },
34682     
34683     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34684     {
34685         Roo.each(eItems, function(b,k){
34686             
34687             b.size = (k == 0) ? 'sm' : 'xs';
34688             b.x = (k == 0) ? 2 : 1;
34689             b.y = (k == 0) ? 2 : 1;
34690             
34691             b.el.position('absolute');
34692             
34693             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34694                 
34695             b.el.setWidth(width);
34696             
34697             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34698             
34699             b.el.setHeight(height);
34700             
34701         }, this);
34702
34703         var positions = [];
34704         
34705         positions.push({
34706             x : maxX - this.unitWidth * 2 - this.gutter,
34707             y : minY
34708         });
34709         
34710         positions.push({
34711             x : maxX - this.unitWidth,
34712             y : minY + (this.unitWidth + this.gutter) * 2
34713         });
34714         
34715         positions.push({
34716             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34717             y : minY
34718         });
34719         
34720         Roo.each(eItems, function(b,k){
34721             
34722             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34723
34724         }, this);
34725         
34726     },
34727     
34728     getVerticalOneBoxColPositions : function(x, y, box)
34729     {
34730         var pos = [];
34731         
34732         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34733         
34734         if(box[0].size == 'md-left'){
34735             rand = 0;
34736         }
34737         
34738         if(box[0].size == 'md-right'){
34739             rand = 1;
34740         }
34741         
34742         pos.push({
34743             x : x + (this.unitWidth + this.gutter) * rand,
34744             y : y
34745         });
34746         
34747         return pos;
34748     },
34749     
34750     getVerticalTwoBoxColPositions : function(x, y, box)
34751     {
34752         var pos = [];
34753         
34754         if(box[0].size == 'xs'){
34755             
34756             pos.push({
34757                 x : x,
34758                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34759             });
34760
34761             pos.push({
34762                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34763                 y : y
34764             });
34765             
34766             return pos;
34767             
34768         }
34769         
34770         pos.push({
34771             x : x,
34772             y : y
34773         });
34774
34775         pos.push({
34776             x : x + (this.unitWidth + this.gutter) * 2,
34777             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34778         });
34779         
34780         return pos;
34781         
34782     },
34783     
34784     getVerticalThreeBoxColPositions : function(x, y, box)
34785     {
34786         var pos = [];
34787         
34788         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34789             
34790             pos.push({
34791                 x : x,
34792                 y : y
34793             });
34794
34795             pos.push({
34796                 x : x + (this.unitWidth + this.gutter) * 1,
34797                 y : y
34798             });
34799             
34800             pos.push({
34801                 x : x + (this.unitWidth + this.gutter) * 2,
34802                 y : y
34803             });
34804             
34805             return pos;
34806             
34807         }
34808         
34809         if(box[0].size == 'xs' && box[1].size == 'xs'){
34810             
34811             pos.push({
34812                 x : x,
34813                 y : y
34814             });
34815
34816             pos.push({
34817                 x : x,
34818                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34819             });
34820             
34821             pos.push({
34822                 x : x + (this.unitWidth + this.gutter) * 1,
34823                 y : y
34824             });
34825             
34826             return pos;
34827             
34828         }
34829         
34830         pos.push({
34831             x : x,
34832             y : y
34833         });
34834
34835         pos.push({
34836             x : x + (this.unitWidth + this.gutter) * 2,
34837             y : y
34838         });
34839
34840         pos.push({
34841             x : x + (this.unitWidth + this.gutter) * 2,
34842             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34843         });
34844             
34845         return pos;
34846         
34847     },
34848     
34849     getVerticalFourBoxColPositions : function(x, y, box)
34850     {
34851         var pos = [];
34852         
34853         if(box[0].size == 'xs'){
34854             
34855             pos.push({
34856                 x : x,
34857                 y : y
34858             });
34859
34860             pos.push({
34861                 x : x,
34862                 y : y + (this.unitHeight + this.gutter) * 1
34863             });
34864             
34865             pos.push({
34866                 x : x,
34867                 y : y + (this.unitHeight + this.gutter) * 2
34868             });
34869             
34870             pos.push({
34871                 x : x + (this.unitWidth + this.gutter) * 1,
34872                 y : y
34873             });
34874             
34875             return pos;
34876             
34877         }
34878         
34879         pos.push({
34880             x : x,
34881             y : y
34882         });
34883
34884         pos.push({
34885             x : x + (this.unitWidth + this.gutter) * 2,
34886             y : y
34887         });
34888
34889         pos.push({
34890             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34891             y : y + (this.unitHeight + this.gutter) * 1
34892         });
34893
34894         pos.push({
34895             x : x + (this.unitWidth + this.gutter) * 2,
34896             y : y + (this.unitWidth + this.gutter) * 2
34897         });
34898
34899         return pos;
34900         
34901     },
34902     
34903     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34904     {
34905         var pos = [];
34906         
34907         if(box[0].size == 'md-left'){
34908             pos.push({
34909                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34910                 y : minY
34911             });
34912             
34913             return pos;
34914         }
34915         
34916         if(box[0].size == 'md-right'){
34917             pos.push({
34918                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34919                 y : minY + (this.unitWidth + this.gutter) * 1
34920             });
34921             
34922             return pos;
34923         }
34924         
34925         var rand = Math.floor(Math.random() * (4 - box[0].y));
34926         
34927         pos.push({
34928             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34929             y : minY + (this.unitWidth + this.gutter) * rand
34930         });
34931         
34932         return pos;
34933         
34934     },
34935     
34936     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34937     {
34938         var pos = [];
34939         
34940         if(box[0].size == 'xs'){
34941             
34942             pos.push({
34943                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34944                 y : minY
34945             });
34946
34947             pos.push({
34948                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34949                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34950             });
34951             
34952             return pos;
34953             
34954         }
34955         
34956         pos.push({
34957             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34958             y : minY
34959         });
34960
34961         pos.push({
34962             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963             y : minY + (this.unitWidth + this.gutter) * 2
34964         });
34965         
34966         return pos;
34967         
34968     },
34969     
34970     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34971     {
34972         var pos = [];
34973         
34974         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34975             
34976             pos.push({
34977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34978                 y : minY
34979             });
34980
34981             pos.push({
34982                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34983                 y : minY + (this.unitWidth + this.gutter) * 1
34984             });
34985             
34986             pos.push({
34987                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34988                 y : minY + (this.unitWidth + this.gutter) * 2
34989             });
34990             
34991             return pos;
34992             
34993         }
34994         
34995         if(box[0].size == 'xs' && box[1].size == 'xs'){
34996             
34997             pos.push({
34998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34999                 y : minY
35000             });
35001
35002             pos.push({
35003                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004                 y : minY
35005             });
35006             
35007             pos.push({
35008                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009                 y : minY + (this.unitWidth + this.gutter) * 1
35010             });
35011             
35012             return pos;
35013             
35014         }
35015         
35016         pos.push({
35017             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35018             y : minY
35019         });
35020
35021         pos.push({
35022             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35023             y : minY + (this.unitWidth + this.gutter) * 2
35024         });
35025
35026         pos.push({
35027             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35028             y : minY + (this.unitWidth + this.gutter) * 2
35029         });
35030             
35031         return pos;
35032         
35033     },
35034     
35035     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35036     {
35037         var pos = [];
35038         
35039         if(box[0].size == 'xs'){
35040             
35041             pos.push({
35042                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35043                 y : minY
35044             });
35045
35046             pos.push({
35047                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35048                 y : minY
35049             });
35050             
35051             pos.push({
35052                 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),
35053                 y : minY
35054             });
35055             
35056             pos.push({
35057                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35058                 y : minY + (this.unitWidth + this.gutter) * 1
35059             });
35060             
35061             return pos;
35062             
35063         }
35064         
35065         pos.push({
35066             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35067             y : minY
35068         });
35069         
35070         pos.push({
35071             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35072             y : minY + (this.unitWidth + this.gutter) * 2
35073         });
35074         
35075         pos.push({
35076             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35077             y : minY + (this.unitWidth + this.gutter) * 2
35078         });
35079         
35080         pos.push({
35081             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),
35082             y : minY + (this.unitWidth + this.gutter) * 2
35083         });
35084
35085         return pos;
35086         
35087     },
35088     
35089     /**
35090     * remove a Masonry Brick
35091     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35092     */
35093     removeBrick : function(brick_id)
35094     {
35095         if (!brick_id) {
35096             return;
35097         }
35098         
35099         for (var i = 0; i<this.bricks.length; i++) {
35100             if (this.bricks[i].id == brick_id) {
35101                 this.bricks.splice(i,1);
35102                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35103                 this.initial();
35104             }
35105         }
35106     },
35107     
35108     /**
35109     * adds a Masonry Brick
35110     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35111     */
35112     addBrick : function(cfg)
35113     {
35114         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35115         //this.register(cn);
35116         cn.parentId = this.id;
35117         cn.render(this.el);
35118         return cn;
35119     },
35120     
35121     /**
35122     * register a Masonry Brick
35123     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35124     */
35125     
35126     register : function(brick)
35127     {
35128         this.bricks.push(brick);
35129         brick.masonryId = this.id;
35130     },
35131     
35132     /**
35133     * clear all the Masonry Brick
35134     */
35135     clearAll : function()
35136     {
35137         this.bricks = [];
35138         //this.getChildContainer().dom.innerHTML = "";
35139         this.el.dom.innerHTML = '';
35140     },
35141     
35142     getSelected : function()
35143     {
35144         if (!this.selectedBrick) {
35145             return false;
35146         }
35147         
35148         return this.selectedBrick;
35149     }
35150 });
35151
35152 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35153     
35154     groups: {},
35155      /**
35156     * register a Masonry Layout
35157     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35158     */
35159     
35160     register : function(layout)
35161     {
35162         this.groups[layout.id] = layout;
35163     },
35164     /**
35165     * fetch a  Masonry Layout based on the masonry layout ID
35166     * @param {string} the masonry layout to add
35167     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35168     */
35169     
35170     get: function(layout_id) {
35171         if (typeof(this.groups[layout_id]) == 'undefined') {
35172             return false;
35173         }
35174         return this.groups[layout_id] ;
35175     }
35176     
35177     
35178     
35179 });
35180
35181  
35182
35183  /**
35184  *
35185  * This is based on 
35186  * http://masonry.desandro.com
35187  *
35188  * The idea is to render all the bricks based on vertical width...
35189  *
35190  * The original code extends 'outlayer' - we might need to use that....
35191  * 
35192  */
35193
35194
35195 /**
35196  * @class Roo.bootstrap.LayoutMasonryAuto
35197  * @extends Roo.bootstrap.Component
35198  * Bootstrap Layout Masonry class
35199  * 
35200  * @constructor
35201  * Create a new Element
35202  * @param {Object} config The config object
35203  */
35204
35205 Roo.bootstrap.LayoutMasonryAuto = function(config){
35206     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35207 };
35208
35209 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35210     
35211       /**
35212      * @cfg {Boolean} isFitWidth  - resize the width..
35213      */   
35214     isFitWidth : false,  // options..
35215     /**
35216      * @cfg {Boolean} isOriginLeft = left align?
35217      */   
35218     isOriginLeft : true,
35219     /**
35220      * @cfg {Boolean} isOriginTop = top align?
35221      */   
35222     isOriginTop : false,
35223     /**
35224      * @cfg {Boolean} isLayoutInstant = no animation?
35225      */   
35226     isLayoutInstant : false, // needed?
35227     /**
35228      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35229      */   
35230     isResizingContainer : true,
35231     /**
35232      * @cfg {Number} columnWidth  width of the columns 
35233      */   
35234     
35235     columnWidth : 0,
35236     
35237     /**
35238      * @cfg {Number} maxCols maximum number of columns
35239      */   
35240     
35241     maxCols: 0,
35242     /**
35243      * @cfg {Number} padHeight padding below box..
35244      */   
35245     
35246     padHeight : 10, 
35247     
35248     /**
35249      * @cfg {Boolean} isAutoInitial defalut true
35250      */   
35251     
35252     isAutoInitial : true, 
35253     
35254     // private?
35255     gutter : 0,
35256     
35257     containerWidth: 0,
35258     initialColumnWidth : 0,
35259     currentSize : null,
35260     
35261     colYs : null, // array.
35262     maxY : 0,
35263     padWidth: 10,
35264     
35265     
35266     tag: 'div',
35267     cls: '',
35268     bricks: null, //CompositeElement
35269     cols : 0, // array?
35270     // element : null, // wrapped now this.el
35271     _isLayoutInited : null, 
35272     
35273     
35274     getAutoCreate : function(){
35275         
35276         var cfg = {
35277             tag: this.tag,
35278             cls: 'blog-masonary-wrapper ' + this.cls,
35279             cn : {
35280                 cls : 'mas-boxes masonary'
35281             }
35282         };
35283         
35284         return cfg;
35285     },
35286     
35287     getChildContainer: function( )
35288     {
35289         if (this.boxesEl) {
35290             return this.boxesEl;
35291         }
35292         
35293         this.boxesEl = this.el.select('.mas-boxes').first();
35294         
35295         return this.boxesEl;
35296     },
35297     
35298     
35299     initEvents : function()
35300     {
35301         var _this = this;
35302         
35303         if(this.isAutoInitial){
35304             Roo.log('hook children rendered');
35305             this.on('childrenrendered', function() {
35306                 Roo.log('children rendered');
35307                 _this.initial();
35308             } ,this);
35309         }
35310         
35311     },
35312     
35313     initial : function()
35314     {
35315         this.reloadItems();
35316
35317         this.currentSize = this.el.getBox(true);
35318
35319         /// was window resize... - let's see if this works..
35320         Roo.EventManager.onWindowResize(this.resize, this); 
35321
35322         if(!this.isAutoInitial){
35323             this.layout();
35324             return;
35325         }
35326         
35327         this.layout.defer(500,this);
35328     },
35329     
35330     reloadItems: function()
35331     {
35332         this.bricks = this.el.select('.masonry-brick', true);
35333         
35334         this.bricks.each(function(b) {
35335             //Roo.log(b.getSize());
35336             if (!b.attr('originalwidth')) {
35337                 b.attr('originalwidth',  b.getSize().width);
35338             }
35339             
35340         });
35341         
35342         Roo.log(this.bricks.elements.length);
35343     },
35344     
35345     resize : function()
35346     {
35347         Roo.log('resize');
35348         var cs = this.el.getBox(true);
35349         
35350         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35351             Roo.log("no change in with or X");
35352             return;
35353         }
35354         this.currentSize = cs;
35355         this.layout();
35356     },
35357     
35358     layout : function()
35359     {
35360          Roo.log('layout');
35361         this._resetLayout();
35362         //this._manageStamps();
35363       
35364         // don't animate first layout
35365         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35366         this.layoutItems( isInstant );
35367       
35368         // flag for initalized
35369         this._isLayoutInited = true;
35370     },
35371     
35372     layoutItems : function( isInstant )
35373     {
35374         //var items = this._getItemsForLayout( this.items );
35375         // original code supports filtering layout items.. we just ignore it..
35376         
35377         this._layoutItems( this.bricks , isInstant );
35378       
35379         this._postLayout();
35380     },
35381     _layoutItems : function ( items , isInstant)
35382     {
35383        //this.fireEvent( 'layout', this, items );
35384     
35385
35386         if ( !items || !items.elements.length ) {
35387           // no items, emit event with empty array
35388             return;
35389         }
35390
35391         var queue = [];
35392         items.each(function(item) {
35393             Roo.log("layout item");
35394             Roo.log(item);
35395             // get x/y object from method
35396             var position = this._getItemLayoutPosition( item );
35397             // enqueue
35398             position.item = item;
35399             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35400             queue.push( position );
35401         }, this);
35402       
35403         this._processLayoutQueue( queue );
35404     },
35405     /** Sets position of item in DOM
35406     * @param {Element} item
35407     * @param {Number} x - horizontal position
35408     * @param {Number} y - vertical position
35409     * @param {Boolean} isInstant - disables transitions
35410     */
35411     _processLayoutQueue : function( queue )
35412     {
35413         for ( var i=0, len = queue.length; i < len; i++ ) {
35414             var obj = queue[i];
35415             obj.item.position('absolute');
35416             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35417         }
35418     },
35419       
35420     
35421     /**
35422     * Any logic you want to do after each layout,
35423     * i.e. size the container
35424     */
35425     _postLayout : function()
35426     {
35427         this.resizeContainer();
35428     },
35429     
35430     resizeContainer : function()
35431     {
35432         if ( !this.isResizingContainer ) {
35433             return;
35434         }
35435         var size = this._getContainerSize();
35436         if ( size ) {
35437             this.el.setSize(size.width,size.height);
35438             this.boxesEl.setSize(size.width,size.height);
35439         }
35440     },
35441     
35442     
35443     
35444     _resetLayout : function()
35445     {
35446         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35447         this.colWidth = this.el.getWidth();
35448         //this.gutter = this.el.getWidth(); 
35449         
35450         this.measureColumns();
35451
35452         // reset column Y
35453         var i = this.cols;
35454         this.colYs = [];
35455         while (i--) {
35456             this.colYs.push( 0 );
35457         }
35458     
35459         this.maxY = 0;
35460     },
35461
35462     measureColumns : function()
35463     {
35464         this.getContainerWidth();
35465       // if columnWidth is 0, default to outerWidth of first item
35466         if ( !this.columnWidth ) {
35467             var firstItem = this.bricks.first();
35468             Roo.log(firstItem);
35469             this.columnWidth  = this.containerWidth;
35470             if (firstItem && firstItem.attr('originalwidth') ) {
35471                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35472             }
35473             // columnWidth fall back to item of first element
35474             Roo.log("set column width?");
35475                         this.initialColumnWidth = this.columnWidth  ;
35476
35477             // if first elem has no width, default to size of container
35478             
35479         }
35480         
35481         
35482         if (this.initialColumnWidth) {
35483             this.columnWidth = this.initialColumnWidth;
35484         }
35485         
35486         
35487             
35488         // column width is fixed at the top - however if container width get's smaller we should
35489         // reduce it...
35490         
35491         // this bit calcs how man columns..
35492             
35493         var columnWidth = this.columnWidth += this.gutter;
35494       
35495         // calculate columns
35496         var containerWidth = this.containerWidth + this.gutter;
35497         
35498         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35499         // fix rounding errors, typically with gutters
35500         var excess = columnWidth - containerWidth % columnWidth;
35501         
35502         
35503         // if overshoot is less than a pixel, round up, otherwise floor it
35504         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35505         cols = Math[ mathMethod ]( cols );
35506         this.cols = Math.max( cols, 1 );
35507         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35508         
35509          // padding positioning..
35510         var totalColWidth = this.cols * this.columnWidth;
35511         var padavail = this.containerWidth - totalColWidth;
35512         // so for 2 columns - we need 3 'pads'
35513         
35514         var padNeeded = (1+this.cols) * this.padWidth;
35515         
35516         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35517         
35518         this.columnWidth += padExtra
35519         //this.padWidth = Math.floor(padavail /  ( this.cols));
35520         
35521         // adjust colum width so that padding is fixed??
35522         
35523         // we have 3 columns ... total = width * 3
35524         // we have X left over... that should be used by 
35525         
35526         //if (this.expandC) {
35527             
35528         //}
35529         
35530         
35531         
35532     },
35533     
35534     getContainerWidth : function()
35535     {
35536        /* // container is parent if fit width
35537         var container = this.isFitWidth ? this.element.parentNode : this.element;
35538         // check that this.size and size are there
35539         // IE8 triggers resize on body size change, so they might not be
35540         
35541         var size = getSize( container );  //FIXME
35542         this.containerWidth = size && size.innerWidth; //FIXME
35543         */
35544          
35545         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35546         
35547     },
35548     
35549     _getItemLayoutPosition : function( item )  // what is item?
35550     {
35551         // we resize the item to our columnWidth..
35552       
35553         item.setWidth(this.columnWidth);
35554         item.autoBoxAdjust  = false;
35555         
35556         var sz = item.getSize();
35557  
35558         // how many columns does this brick span
35559         var remainder = this.containerWidth % this.columnWidth;
35560         
35561         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35562         // round if off by 1 pixel, otherwise use ceil
35563         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35564         colSpan = Math.min( colSpan, this.cols );
35565         
35566         // normally this should be '1' as we dont' currently allow multi width columns..
35567         
35568         var colGroup = this._getColGroup( colSpan );
35569         // get the minimum Y value from the columns
35570         var minimumY = Math.min.apply( Math, colGroup );
35571         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35572         
35573         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35574          
35575         // position the brick
35576         var position = {
35577             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35578             y: this.currentSize.y + minimumY + this.padHeight
35579         };
35580         
35581         Roo.log(position);
35582         // apply setHeight to necessary columns
35583         var setHeight = minimumY + sz.height + this.padHeight;
35584         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35585         
35586         var setSpan = this.cols + 1 - colGroup.length;
35587         for ( var i = 0; i < setSpan; i++ ) {
35588           this.colYs[ shortColIndex + i ] = setHeight ;
35589         }
35590       
35591         return position;
35592     },
35593     
35594     /**
35595      * @param {Number} colSpan - number of columns the element spans
35596      * @returns {Array} colGroup
35597      */
35598     _getColGroup : function( colSpan )
35599     {
35600         if ( colSpan < 2 ) {
35601           // if brick spans only one column, use all the column Ys
35602           return this.colYs;
35603         }
35604       
35605         var colGroup = [];
35606         // how many different places could this brick fit horizontally
35607         var groupCount = this.cols + 1 - colSpan;
35608         // for each group potential horizontal position
35609         for ( var i = 0; i < groupCount; i++ ) {
35610           // make an array of colY values for that one group
35611           var groupColYs = this.colYs.slice( i, i + colSpan );
35612           // and get the max value of the array
35613           colGroup[i] = Math.max.apply( Math, groupColYs );
35614         }
35615         return colGroup;
35616     },
35617     /*
35618     _manageStamp : function( stamp )
35619     {
35620         var stampSize =  stamp.getSize();
35621         var offset = stamp.getBox();
35622         // get the columns that this stamp affects
35623         var firstX = this.isOriginLeft ? offset.x : offset.right;
35624         var lastX = firstX + stampSize.width;
35625         var firstCol = Math.floor( firstX / this.columnWidth );
35626         firstCol = Math.max( 0, firstCol );
35627         
35628         var lastCol = Math.floor( lastX / this.columnWidth );
35629         // lastCol should not go over if multiple of columnWidth #425
35630         lastCol -= lastX % this.columnWidth ? 0 : 1;
35631         lastCol = Math.min( this.cols - 1, lastCol );
35632         
35633         // set colYs to bottom of the stamp
35634         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35635             stampSize.height;
35636             
35637         for ( var i = firstCol; i <= lastCol; i++ ) {
35638           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35639         }
35640     },
35641     */
35642     
35643     _getContainerSize : function()
35644     {
35645         this.maxY = Math.max.apply( Math, this.colYs );
35646         var size = {
35647             height: this.maxY
35648         };
35649       
35650         if ( this.isFitWidth ) {
35651             size.width = this._getContainerFitWidth();
35652         }
35653       
35654         return size;
35655     },
35656     
35657     _getContainerFitWidth : function()
35658     {
35659         var unusedCols = 0;
35660         // count unused columns
35661         var i = this.cols;
35662         while ( --i ) {
35663           if ( this.colYs[i] !== 0 ) {
35664             break;
35665           }
35666           unusedCols++;
35667         }
35668         // fit container to columns that have been used
35669         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35670     },
35671     
35672     needsResizeLayout : function()
35673     {
35674         var previousWidth = this.containerWidth;
35675         this.getContainerWidth();
35676         return previousWidth !== this.containerWidth;
35677     }
35678  
35679 });
35680
35681  
35682
35683  /*
35684  * - LGPL
35685  *
35686  * element
35687  * 
35688  */
35689
35690 /**
35691  * @class Roo.bootstrap.MasonryBrick
35692  * @extends Roo.bootstrap.Component
35693  * Bootstrap MasonryBrick class
35694  * 
35695  * @constructor
35696  * Create a new MasonryBrick
35697  * @param {Object} config The config object
35698  */
35699
35700 Roo.bootstrap.MasonryBrick = function(config){
35701     
35702     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35703     
35704     Roo.bootstrap.MasonryBrick.register(this);
35705     
35706     this.addEvents({
35707         // raw events
35708         /**
35709          * @event click
35710          * When a MasonryBrick is clcik
35711          * @param {Roo.bootstrap.MasonryBrick} this
35712          * @param {Roo.EventObject} e
35713          */
35714         "click" : true
35715     });
35716 };
35717
35718 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35719     
35720     /**
35721      * @cfg {String} title
35722      */   
35723     title : '',
35724     /**
35725      * @cfg {String} html
35726      */   
35727     html : '',
35728     /**
35729      * @cfg {String} bgimage
35730      */   
35731     bgimage : '',
35732     /**
35733      * @cfg {String} videourl
35734      */   
35735     videourl : '',
35736     /**
35737      * @cfg {String} cls
35738      */   
35739     cls : '',
35740     /**
35741      * @cfg {String} href
35742      */   
35743     href : '',
35744     /**
35745      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35746      */   
35747     size : 'xs',
35748     
35749     /**
35750      * @cfg {String} placetitle (center|bottom)
35751      */   
35752     placetitle : '',
35753     
35754     /**
35755      * @cfg {Boolean} isFitContainer defalut true
35756      */   
35757     isFitContainer : true, 
35758     
35759     /**
35760      * @cfg {Boolean} preventDefault defalut false
35761      */   
35762     preventDefault : false, 
35763     
35764     /**
35765      * @cfg {Boolean} inverse defalut false
35766      */   
35767     maskInverse : false, 
35768     
35769     getAutoCreate : function()
35770     {
35771         if(!this.isFitContainer){
35772             return this.getSplitAutoCreate();
35773         }
35774         
35775         var cls = 'masonry-brick masonry-brick-full';
35776         
35777         if(this.href.length){
35778             cls += ' masonry-brick-link';
35779         }
35780         
35781         if(this.bgimage.length){
35782             cls += ' masonry-brick-image';
35783         }
35784         
35785         if(this.maskInverse){
35786             cls += ' mask-inverse';
35787         }
35788         
35789         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35790             cls += ' enable-mask';
35791         }
35792         
35793         if(this.size){
35794             cls += ' masonry-' + this.size + '-brick';
35795         }
35796         
35797         if(this.placetitle.length){
35798             
35799             switch (this.placetitle) {
35800                 case 'center' :
35801                     cls += ' masonry-center-title';
35802                     break;
35803                 case 'bottom' :
35804                     cls += ' masonry-bottom-title';
35805                     break;
35806                 default:
35807                     break;
35808             }
35809             
35810         } else {
35811             if(!this.html.length && !this.bgimage.length){
35812                 cls += ' masonry-center-title';
35813             }
35814
35815             if(!this.html.length && this.bgimage.length){
35816                 cls += ' masonry-bottom-title';
35817             }
35818         }
35819         
35820         if(this.cls){
35821             cls += ' ' + this.cls;
35822         }
35823         
35824         var cfg = {
35825             tag: (this.href.length) ? 'a' : 'div',
35826             cls: cls,
35827             cn: [
35828                 {
35829                     tag: 'div',
35830                     cls: 'masonry-brick-mask'
35831                 },
35832                 {
35833                     tag: 'div',
35834                     cls: 'masonry-brick-paragraph',
35835                     cn: []
35836                 }
35837             ]
35838         };
35839         
35840         if(this.href.length){
35841             cfg.href = this.href;
35842         }
35843         
35844         var cn = cfg.cn[1].cn;
35845         
35846         if(this.title.length){
35847             cn.push({
35848                 tag: 'h4',
35849                 cls: 'masonry-brick-title',
35850                 html: this.title
35851             });
35852         }
35853         
35854         if(this.html.length){
35855             cn.push({
35856                 tag: 'p',
35857                 cls: 'masonry-brick-text',
35858                 html: this.html
35859             });
35860         }
35861         
35862         if (!this.title.length && !this.html.length) {
35863             cfg.cn[1].cls += ' hide';
35864         }
35865         
35866         if(this.bgimage.length){
35867             cfg.cn.push({
35868                 tag: 'img',
35869                 cls: 'masonry-brick-image-view',
35870                 src: this.bgimage
35871             });
35872         }
35873         
35874         if(this.videourl.length){
35875             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35876             // youtube support only?
35877             cfg.cn.push({
35878                 tag: 'iframe',
35879                 cls: 'masonry-brick-image-view',
35880                 src: vurl,
35881                 frameborder : 0,
35882                 allowfullscreen : true
35883             });
35884         }
35885         
35886         return cfg;
35887         
35888     },
35889     
35890     getSplitAutoCreate : function()
35891     {
35892         var cls = 'masonry-brick masonry-brick-split';
35893         
35894         if(this.href.length){
35895             cls += ' masonry-brick-link';
35896         }
35897         
35898         if(this.bgimage.length){
35899             cls += ' masonry-brick-image';
35900         }
35901         
35902         if(this.size){
35903             cls += ' masonry-' + this.size + '-brick';
35904         }
35905         
35906         switch (this.placetitle) {
35907             case 'center' :
35908                 cls += ' masonry-center-title';
35909                 break;
35910             case 'bottom' :
35911                 cls += ' masonry-bottom-title';
35912                 break;
35913             default:
35914                 if(!this.bgimage.length){
35915                     cls += ' masonry-center-title';
35916                 }
35917
35918                 if(this.bgimage.length){
35919                     cls += ' masonry-bottom-title';
35920                 }
35921                 break;
35922         }
35923         
35924         if(this.cls){
35925             cls += ' ' + this.cls;
35926         }
35927         
35928         var cfg = {
35929             tag: (this.href.length) ? 'a' : 'div',
35930             cls: cls,
35931             cn: [
35932                 {
35933                     tag: 'div',
35934                     cls: 'masonry-brick-split-head',
35935                     cn: [
35936                         {
35937                             tag: 'div',
35938                             cls: 'masonry-brick-paragraph',
35939                             cn: []
35940                         }
35941                     ]
35942                 },
35943                 {
35944                     tag: 'div',
35945                     cls: 'masonry-brick-split-body',
35946                     cn: []
35947                 }
35948             ]
35949         };
35950         
35951         if(this.href.length){
35952             cfg.href = this.href;
35953         }
35954         
35955         if(this.title.length){
35956             cfg.cn[0].cn[0].cn.push({
35957                 tag: 'h4',
35958                 cls: 'masonry-brick-title',
35959                 html: this.title
35960             });
35961         }
35962         
35963         if(this.html.length){
35964             cfg.cn[1].cn.push({
35965                 tag: 'p',
35966                 cls: 'masonry-brick-text',
35967                 html: this.html
35968             });
35969         }
35970
35971         if(this.bgimage.length){
35972             cfg.cn[0].cn.push({
35973                 tag: 'img',
35974                 cls: 'masonry-brick-image-view',
35975                 src: this.bgimage
35976             });
35977         }
35978         
35979         if(this.videourl.length){
35980             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35981             // youtube support only?
35982             cfg.cn[0].cn.cn.push({
35983                 tag: 'iframe',
35984                 cls: 'masonry-brick-image-view',
35985                 src: vurl,
35986                 frameborder : 0,
35987                 allowfullscreen : true
35988             });
35989         }
35990         
35991         return cfg;
35992     },
35993     
35994     initEvents: function() 
35995     {
35996         switch (this.size) {
35997             case 'xs' :
35998                 this.x = 1;
35999                 this.y = 1;
36000                 break;
36001             case 'sm' :
36002                 this.x = 2;
36003                 this.y = 2;
36004                 break;
36005             case 'md' :
36006             case 'md-left' :
36007             case 'md-right' :
36008                 this.x = 3;
36009                 this.y = 3;
36010                 break;
36011             case 'tall' :
36012                 this.x = 2;
36013                 this.y = 3;
36014                 break;
36015             case 'wide' :
36016                 this.x = 3;
36017                 this.y = 2;
36018                 break;
36019             case 'wide-thin' :
36020                 this.x = 3;
36021                 this.y = 1;
36022                 break;
36023                         
36024             default :
36025                 break;
36026         }
36027         
36028         if(Roo.isTouch){
36029             this.el.on('touchstart', this.onTouchStart, this);
36030             this.el.on('touchmove', this.onTouchMove, this);
36031             this.el.on('touchend', this.onTouchEnd, this);
36032             this.el.on('contextmenu', this.onContextMenu, this);
36033         } else {
36034             this.el.on('mouseenter'  ,this.enter, this);
36035             this.el.on('mouseleave', this.leave, this);
36036             this.el.on('click', this.onClick, this);
36037         }
36038         
36039         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36040             this.parent().bricks.push(this);   
36041         }
36042         
36043     },
36044     
36045     onClick: function(e, el)
36046     {
36047         var time = this.endTimer - this.startTimer;
36048         // Roo.log(e.preventDefault());
36049         if(Roo.isTouch){
36050             if(time > 1000){
36051                 e.preventDefault();
36052                 return;
36053             }
36054         }
36055         
36056         if(!this.preventDefault){
36057             return;
36058         }
36059         
36060         e.preventDefault();
36061         
36062         if (this.activeClass != '') {
36063             this.selectBrick();
36064         }
36065         
36066         this.fireEvent('click', this, e);
36067     },
36068     
36069     enter: function(e, el)
36070     {
36071         e.preventDefault();
36072         
36073         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36074             return;
36075         }
36076         
36077         if(this.bgimage.length && this.html.length){
36078             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36079         }
36080     },
36081     
36082     leave: function(e, el)
36083     {
36084         e.preventDefault();
36085         
36086         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36087             return;
36088         }
36089         
36090         if(this.bgimage.length && this.html.length){
36091             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36092         }
36093     },
36094     
36095     onTouchStart: function(e, el)
36096     {
36097 //        e.preventDefault();
36098         
36099         this.touchmoved = false;
36100         
36101         if(!this.isFitContainer){
36102             return;
36103         }
36104         
36105         if(!this.bgimage.length || !this.html.length){
36106             return;
36107         }
36108         
36109         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36110         
36111         this.timer = new Date().getTime();
36112         
36113     },
36114     
36115     onTouchMove: function(e, el)
36116     {
36117         this.touchmoved = true;
36118     },
36119     
36120     onContextMenu : function(e,el)
36121     {
36122         e.preventDefault();
36123         e.stopPropagation();
36124         return false;
36125     },
36126     
36127     onTouchEnd: function(e, el)
36128     {
36129 //        e.preventDefault();
36130         
36131         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36132         
36133             this.leave(e,el);
36134             
36135             return;
36136         }
36137         
36138         if(!this.bgimage.length || !this.html.length){
36139             
36140             if(this.href.length){
36141                 window.location.href = this.href;
36142             }
36143             
36144             return;
36145         }
36146         
36147         if(!this.isFitContainer){
36148             return;
36149         }
36150         
36151         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36152         
36153         window.location.href = this.href;
36154     },
36155     
36156     //selection on single brick only
36157     selectBrick : function() {
36158         
36159         if (!this.parentId) {
36160             return;
36161         }
36162         
36163         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36164         var index = m.selectedBrick.indexOf(this.id);
36165         
36166         if ( index > -1) {
36167             m.selectedBrick.splice(index,1);
36168             this.el.removeClass(this.activeClass);
36169             return;
36170         }
36171         
36172         for(var i = 0; i < m.selectedBrick.length; i++) {
36173             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36174             b.el.removeClass(b.activeClass);
36175         }
36176         
36177         m.selectedBrick = [];
36178         
36179         m.selectedBrick.push(this.id);
36180         this.el.addClass(this.activeClass);
36181         return;
36182     },
36183     
36184     isSelected : function(){
36185         return this.el.hasClass(this.activeClass);
36186         
36187     }
36188 });
36189
36190 Roo.apply(Roo.bootstrap.MasonryBrick, {
36191     
36192     //groups: {},
36193     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36194      /**
36195     * register a Masonry Brick
36196     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36197     */
36198     
36199     register : function(brick)
36200     {
36201         //this.groups[brick.id] = brick;
36202         this.groups.add(brick.id, brick);
36203     },
36204     /**
36205     * fetch a  masonry brick based on the masonry brick ID
36206     * @param {string} the masonry brick to add
36207     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36208     */
36209     
36210     get: function(brick_id) 
36211     {
36212         // if (typeof(this.groups[brick_id]) == 'undefined') {
36213         //     return false;
36214         // }
36215         // return this.groups[brick_id] ;
36216         
36217         if(this.groups.key(brick_id)) {
36218             return this.groups.key(brick_id);
36219         }
36220         
36221         return false;
36222     }
36223     
36224     
36225     
36226 });
36227
36228  /*
36229  * - LGPL
36230  *
36231  * element
36232  * 
36233  */
36234
36235 /**
36236  * @class Roo.bootstrap.Brick
36237  * @extends Roo.bootstrap.Component
36238  * Bootstrap Brick class
36239  * 
36240  * @constructor
36241  * Create a new Brick
36242  * @param {Object} config The config object
36243  */
36244
36245 Roo.bootstrap.Brick = function(config){
36246     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36247     
36248     this.addEvents({
36249         // raw events
36250         /**
36251          * @event click
36252          * When a Brick is click
36253          * @param {Roo.bootstrap.Brick} this
36254          * @param {Roo.EventObject} e
36255          */
36256         "click" : true
36257     });
36258 };
36259
36260 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36261     
36262     /**
36263      * @cfg {String} title
36264      */   
36265     title : '',
36266     /**
36267      * @cfg {String} html
36268      */   
36269     html : '',
36270     /**
36271      * @cfg {String} bgimage
36272      */   
36273     bgimage : '',
36274     /**
36275      * @cfg {String} cls
36276      */   
36277     cls : '',
36278     /**
36279      * @cfg {String} href
36280      */   
36281     href : '',
36282     /**
36283      * @cfg {String} video
36284      */   
36285     video : '',
36286     /**
36287      * @cfg {Boolean} square
36288      */   
36289     square : true,
36290     
36291     getAutoCreate : function()
36292     {
36293         var cls = 'roo-brick';
36294         
36295         if(this.href.length){
36296             cls += ' roo-brick-link';
36297         }
36298         
36299         if(this.bgimage.length){
36300             cls += ' roo-brick-image';
36301         }
36302         
36303         if(!this.html.length && !this.bgimage.length){
36304             cls += ' roo-brick-center-title';
36305         }
36306         
36307         if(!this.html.length && this.bgimage.length){
36308             cls += ' roo-brick-bottom-title';
36309         }
36310         
36311         if(this.cls){
36312             cls += ' ' + this.cls;
36313         }
36314         
36315         var cfg = {
36316             tag: (this.href.length) ? 'a' : 'div',
36317             cls: cls,
36318             cn: [
36319                 {
36320                     tag: 'div',
36321                     cls: 'roo-brick-paragraph',
36322                     cn: []
36323                 }
36324             ]
36325         };
36326         
36327         if(this.href.length){
36328             cfg.href = this.href;
36329         }
36330         
36331         var cn = cfg.cn[0].cn;
36332         
36333         if(this.title.length){
36334             cn.push({
36335                 tag: 'h4',
36336                 cls: 'roo-brick-title',
36337                 html: this.title
36338             });
36339         }
36340         
36341         if(this.html.length){
36342             cn.push({
36343                 tag: 'p',
36344                 cls: 'roo-brick-text',
36345                 html: this.html
36346             });
36347         } else {
36348             cn.cls += ' hide';
36349         }
36350         
36351         if(this.bgimage.length){
36352             cfg.cn.push({
36353                 tag: 'img',
36354                 cls: 'roo-brick-image-view',
36355                 src: this.bgimage
36356             });
36357         }
36358         
36359         return cfg;
36360     },
36361     
36362     initEvents: function() 
36363     {
36364         if(this.title.length || this.html.length){
36365             this.el.on('mouseenter'  ,this.enter, this);
36366             this.el.on('mouseleave', this.leave, this);
36367         }
36368         
36369         Roo.EventManager.onWindowResize(this.resize, this); 
36370         
36371         if(this.bgimage.length){
36372             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36373             this.imageEl.on('load', this.onImageLoad, this);
36374             return;
36375         }
36376         
36377         this.resize();
36378     },
36379     
36380     onImageLoad : function()
36381     {
36382         this.resize();
36383     },
36384     
36385     resize : function()
36386     {
36387         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36388         
36389         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36390         
36391         if(this.bgimage.length){
36392             var image = this.el.select('.roo-brick-image-view', true).first();
36393             
36394             image.setWidth(paragraph.getWidth());
36395             
36396             if(this.square){
36397                 image.setHeight(paragraph.getWidth());
36398             }
36399             
36400             this.el.setHeight(image.getHeight());
36401             paragraph.setHeight(image.getHeight());
36402             
36403         }
36404         
36405     },
36406     
36407     enter: function(e, el)
36408     {
36409         e.preventDefault();
36410         
36411         if(this.bgimage.length){
36412             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36413             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36414         }
36415     },
36416     
36417     leave: function(e, el)
36418     {
36419         e.preventDefault();
36420         
36421         if(this.bgimage.length){
36422             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36423             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36424         }
36425     }
36426     
36427 });
36428
36429  
36430
36431  /*
36432  * - LGPL
36433  *
36434  * Number field 
36435  */
36436
36437 /**
36438  * @class Roo.bootstrap.NumberField
36439  * @extends Roo.bootstrap.Input
36440  * Bootstrap NumberField class
36441  * 
36442  * 
36443  * 
36444  * 
36445  * @constructor
36446  * Create a new NumberField
36447  * @param {Object} config The config object
36448  */
36449
36450 Roo.bootstrap.NumberField = function(config){
36451     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36452 };
36453
36454 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36455     
36456     /**
36457      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36458      */
36459     allowDecimals : true,
36460     /**
36461      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36462      */
36463     decimalSeparator : ".",
36464     /**
36465      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36466      */
36467     decimalPrecision : 2,
36468     /**
36469      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36470      */
36471     allowNegative : true,
36472     
36473     /**
36474      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36475      */
36476     allowZero: true,
36477     /**
36478      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36479      */
36480     minValue : Number.NEGATIVE_INFINITY,
36481     /**
36482      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36483      */
36484     maxValue : Number.MAX_VALUE,
36485     /**
36486      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36487      */
36488     minText : "The minimum value for this field is {0}",
36489     /**
36490      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36491      */
36492     maxText : "The maximum value for this field is {0}",
36493     /**
36494      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36495      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36496      */
36497     nanText : "{0} is not a valid number",
36498     /**
36499      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36500      */
36501     thousandsDelimiter : false,
36502     /**
36503      * @cfg {String} valueAlign alignment of value
36504      */
36505     valueAlign : "left",
36506
36507     getAutoCreate : function()
36508     {
36509         var hiddenInput = {
36510             tag: 'input',
36511             type: 'hidden',
36512             id: Roo.id(),
36513             cls: 'hidden-number-input'
36514         };
36515         
36516         if (this.name) {
36517             hiddenInput.name = this.name;
36518         }
36519         
36520         this.name = '';
36521         
36522         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36523         
36524         this.name = hiddenInput.name;
36525         
36526         if(cfg.cn.length > 0) {
36527             cfg.cn.push(hiddenInput);
36528         }
36529         
36530         return cfg;
36531     },
36532
36533     // private
36534     initEvents : function()
36535     {   
36536         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36537         
36538         var allowed = "0123456789";
36539         
36540         if(this.allowDecimals){
36541             allowed += this.decimalSeparator;
36542         }
36543         
36544         if(this.allowNegative){
36545             allowed += "-";
36546         }
36547         
36548         if(this.thousandsDelimiter) {
36549             allowed += ",";
36550         }
36551         
36552         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36553         
36554         var keyPress = function(e){
36555             
36556             var k = e.getKey();
36557             
36558             var c = e.getCharCode();
36559             
36560             if(
36561                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36562                     allowed.indexOf(String.fromCharCode(c)) === -1
36563             ){
36564                 e.stopEvent();
36565                 return;
36566             }
36567             
36568             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36569                 return;
36570             }
36571             
36572             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36573                 e.stopEvent();
36574             }
36575         };
36576         
36577         this.el.on("keypress", keyPress, this);
36578     },
36579     
36580     validateValue : function(value)
36581     {
36582         
36583         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36584             return false;
36585         }
36586         
36587         var num = this.parseValue(value);
36588         
36589         if(isNaN(num)){
36590             this.markInvalid(String.format(this.nanText, value));
36591             return false;
36592         }
36593         
36594         if(num < this.minValue){
36595             this.markInvalid(String.format(this.minText, this.minValue));
36596             return false;
36597         }
36598         
36599         if(num > this.maxValue){
36600             this.markInvalid(String.format(this.maxText, this.maxValue));
36601             return false;
36602         }
36603         
36604         return true;
36605     },
36606
36607     getValue : function()
36608     {
36609         var v = this.hiddenEl().getValue();
36610         
36611         return this.fixPrecision(this.parseValue(v));
36612     },
36613
36614     parseValue : function(value)
36615     {
36616         if(this.thousandsDelimiter) {
36617             value += "";
36618             r = new RegExp(",", "g");
36619             value = value.replace(r, "");
36620         }
36621         
36622         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36623         return isNaN(value) ? '' : value;
36624     },
36625
36626     fixPrecision : function(value)
36627     {
36628         if(this.thousandsDelimiter) {
36629             value += "";
36630             r = new RegExp(",", "g");
36631             value = value.replace(r, "");
36632         }
36633         
36634         var nan = isNaN(value);
36635         
36636         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36637             return nan ? '' : value;
36638         }
36639         return parseFloat(value).toFixed(this.decimalPrecision);
36640     },
36641
36642     setValue : function(v)
36643     {
36644         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36645         
36646         this.value = v;
36647         
36648         if(this.rendered){
36649             
36650             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36651             
36652             this.inputEl().dom.value = (v == '') ? '' :
36653                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36654             
36655             if(!this.allowZero && v === '0') {
36656                 this.hiddenEl().dom.value = '';
36657                 this.inputEl().dom.value = '';
36658             }
36659             
36660             this.validate();
36661         }
36662     },
36663
36664     decimalPrecisionFcn : function(v)
36665     {
36666         return Math.floor(v);
36667     },
36668
36669     beforeBlur : function()
36670     {
36671         var v = this.parseValue(this.getRawValue());
36672         
36673         if(v || v === 0 || v === ''){
36674             this.setValue(v);
36675         }
36676     },
36677     
36678     hiddenEl : function()
36679     {
36680         return this.el.select('input.hidden-number-input',true).first();
36681     }
36682     
36683 });
36684
36685  
36686
36687 /*
36688 * Licence: LGPL
36689 */
36690
36691 /**
36692  * @class Roo.bootstrap.DocumentSlider
36693  * @extends Roo.bootstrap.Component
36694  * Bootstrap DocumentSlider class
36695  * 
36696  * @constructor
36697  * Create a new DocumentViewer
36698  * @param {Object} config The config object
36699  */
36700
36701 Roo.bootstrap.DocumentSlider = function(config){
36702     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36703     
36704     this.files = [];
36705     
36706     this.addEvents({
36707         /**
36708          * @event initial
36709          * Fire after initEvent
36710          * @param {Roo.bootstrap.DocumentSlider} this
36711          */
36712         "initial" : true,
36713         /**
36714          * @event update
36715          * Fire after update
36716          * @param {Roo.bootstrap.DocumentSlider} this
36717          */
36718         "update" : true,
36719         /**
36720          * @event click
36721          * Fire after click
36722          * @param {Roo.bootstrap.DocumentSlider} this
36723          */
36724         "click" : true
36725     });
36726 };
36727
36728 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36729     
36730     files : false,
36731     
36732     indicator : 0,
36733     
36734     getAutoCreate : function()
36735     {
36736         var cfg = {
36737             tag : 'div',
36738             cls : 'roo-document-slider',
36739             cn : [
36740                 {
36741                     tag : 'div',
36742                     cls : 'roo-document-slider-header',
36743                     cn : [
36744                         {
36745                             tag : 'div',
36746                             cls : 'roo-document-slider-header-title'
36747                         }
36748                     ]
36749                 },
36750                 {
36751                     tag : 'div',
36752                     cls : 'roo-document-slider-body',
36753                     cn : [
36754                         {
36755                             tag : 'div',
36756                             cls : 'roo-document-slider-prev',
36757                             cn : [
36758                                 {
36759                                     tag : 'i',
36760                                     cls : 'fa fa-chevron-left'
36761                                 }
36762                             ]
36763                         },
36764                         {
36765                             tag : 'div',
36766                             cls : 'roo-document-slider-thumb',
36767                             cn : [
36768                                 {
36769                                     tag : 'img',
36770                                     cls : 'roo-document-slider-image'
36771                                 }
36772                             ]
36773                         },
36774                         {
36775                             tag : 'div',
36776                             cls : 'roo-document-slider-next',
36777                             cn : [
36778                                 {
36779                                     tag : 'i',
36780                                     cls : 'fa fa-chevron-right'
36781                                 }
36782                             ]
36783                         }
36784                     ]
36785                 }
36786             ]
36787         };
36788         
36789         return cfg;
36790     },
36791     
36792     initEvents : function()
36793     {
36794         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36795         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36796         
36797         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36798         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36799         
36800         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36801         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36802         
36803         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36804         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36805         
36806         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36807         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36808         
36809         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36810         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36811         
36812         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36813         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36814         
36815         this.thumbEl.on('click', this.onClick, this);
36816         
36817         this.prevIndicator.on('click', this.prev, this);
36818         
36819         this.nextIndicator.on('click', this.next, this);
36820         
36821     },
36822     
36823     initial : function()
36824     {
36825         if(this.files.length){
36826             this.indicator = 1;
36827             this.update()
36828         }
36829         
36830         this.fireEvent('initial', this);
36831     },
36832     
36833     update : function()
36834     {
36835         this.imageEl.attr('src', this.files[this.indicator - 1]);
36836         
36837         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36838         
36839         this.prevIndicator.show();
36840         
36841         if(this.indicator == 1){
36842             this.prevIndicator.hide();
36843         }
36844         
36845         this.nextIndicator.show();
36846         
36847         if(this.indicator == this.files.length){
36848             this.nextIndicator.hide();
36849         }
36850         
36851         this.thumbEl.scrollTo('top');
36852         
36853         this.fireEvent('update', this);
36854     },
36855     
36856     onClick : function(e)
36857     {
36858         e.preventDefault();
36859         
36860         this.fireEvent('click', this);
36861     },
36862     
36863     prev : function(e)
36864     {
36865         e.preventDefault();
36866         
36867         this.indicator = Math.max(1, this.indicator - 1);
36868         
36869         this.update();
36870     },
36871     
36872     next : function(e)
36873     {
36874         e.preventDefault();
36875         
36876         this.indicator = Math.min(this.files.length, this.indicator + 1);
36877         
36878         this.update();
36879     }
36880 });
36881 /*
36882  * - LGPL
36883  *
36884  * RadioSet
36885  *
36886  *
36887  */
36888
36889 /**
36890  * @class Roo.bootstrap.RadioSet
36891  * @extends Roo.bootstrap.Input
36892  * Bootstrap RadioSet class
36893  * @cfg {String} indicatorpos (left|right) default left
36894  * @cfg {Boolean} inline (true|false) inline the element (default true)
36895  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36896  * @constructor
36897  * Create a new RadioSet
36898  * @param {Object} config The config object
36899  */
36900
36901 Roo.bootstrap.RadioSet = function(config){
36902     
36903     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36904     
36905     this.radioes = [];
36906     
36907     Roo.bootstrap.RadioSet.register(this);
36908     
36909     this.addEvents({
36910         /**
36911         * @event check
36912         * Fires when the element is checked or unchecked.
36913         * @param {Roo.bootstrap.RadioSet} this This radio
36914         * @param {Roo.bootstrap.Radio} item The checked item
36915         */
36916        check : true,
36917        /**
36918         * @event click
36919         * Fires when the element is click.
36920         * @param {Roo.bootstrap.RadioSet} this This radio set
36921         * @param {Roo.bootstrap.Radio} item The checked item
36922         * @param {Roo.EventObject} e The event object
36923         */
36924        click : true
36925     });
36926     
36927 };
36928
36929 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36930
36931     radioes : false,
36932     
36933     inline : true,
36934     
36935     weight : '',
36936     
36937     indicatorpos : 'left',
36938     
36939     getAutoCreate : function()
36940     {
36941         var label = {
36942             tag : 'label',
36943             cls : 'roo-radio-set-label',
36944             cn : [
36945                 {
36946                     tag : 'span',
36947                     html : this.fieldLabel
36948                 }
36949             ]
36950         };
36951         if (Roo.bootstrap.version == 3) {
36952             
36953             
36954             if(this.indicatorpos == 'left'){
36955                 label.cn.unshift({
36956                     tag : 'i',
36957                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36958                     tooltip : 'This field is required'
36959                 });
36960             } else {
36961                 label.cn.push({
36962                     tag : 'i',
36963                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36964                     tooltip : 'This field is required'
36965                 });
36966             }
36967         }
36968         var items = {
36969             tag : 'div',
36970             cls : 'roo-radio-set-items'
36971         };
36972         
36973         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36974         
36975         if (align === 'left' && this.fieldLabel.length) {
36976             
36977             items = {
36978                 cls : "roo-radio-set-right", 
36979                 cn: [
36980                     items
36981                 ]
36982             };
36983             
36984             if(this.labelWidth > 12){
36985                 label.style = "width: " + this.labelWidth + 'px';
36986             }
36987             
36988             if(this.labelWidth < 13 && this.labelmd == 0){
36989                 this.labelmd = this.labelWidth;
36990             }
36991             
36992             if(this.labellg > 0){
36993                 label.cls += ' col-lg-' + this.labellg;
36994                 items.cls += ' col-lg-' + (12 - this.labellg);
36995             }
36996             
36997             if(this.labelmd > 0){
36998                 label.cls += ' col-md-' + this.labelmd;
36999                 items.cls += ' col-md-' + (12 - this.labelmd);
37000             }
37001             
37002             if(this.labelsm > 0){
37003                 label.cls += ' col-sm-' + this.labelsm;
37004                 items.cls += ' col-sm-' + (12 - this.labelsm);
37005             }
37006             
37007             if(this.labelxs > 0){
37008                 label.cls += ' col-xs-' + this.labelxs;
37009                 items.cls += ' col-xs-' + (12 - this.labelxs);
37010             }
37011         }
37012         
37013         var cfg = {
37014             tag : 'div',
37015             cls : 'roo-radio-set',
37016             cn : [
37017                 {
37018                     tag : 'input',
37019                     cls : 'roo-radio-set-input',
37020                     type : 'hidden',
37021                     name : this.name,
37022                     value : this.value ? this.value :  ''
37023                 },
37024                 label,
37025                 items
37026             ]
37027         };
37028         
37029         if(this.weight.length){
37030             cfg.cls += ' roo-radio-' + this.weight;
37031         }
37032         
37033         if(this.inline) {
37034             cfg.cls += ' roo-radio-set-inline';
37035         }
37036         
37037         var settings=this;
37038         ['xs','sm','md','lg'].map(function(size){
37039             if (settings[size]) {
37040                 cfg.cls += ' col-' + size + '-' + settings[size];
37041             }
37042         });
37043         
37044         return cfg;
37045         
37046     },
37047
37048     initEvents : function()
37049     {
37050         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37051         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37052         
37053         if(!this.fieldLabel.length){
37054             this.labelEl.hide();
37055         }
37056         
37057         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37058         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37059         
37060         this.indicator = this.indicatorEl();
37061         
37062         if(this.indicator){
37063             this.indicator.addClass('invisible');
37064         }
37065         
37066         this.originalValue = this.getValue();
37067         
37068     },
37069     
37070     inputEl: function ()
37071     {
37072         return this.el.select('.roo-radio-set-input', true).first();
37073     },
37074     
37075     getChildContainer : function()
37076     {
37077         return this.itemsEl;
37078     },
37079     
37080     register : function(item)
37081     {
37082         this.radioes.push(item);
37083         
37084     },
37085     
37086     validate : function()
37087     {   
37088         if(this.getVisibilityEl().hasClass('hidden')){
37089             return true;
37090         }
37091         
37092         var valid = false;
37093         
37094         Roo.each(this.radioes, function(i){
37095             if(!i.checked){
37096                 return;
37097             }
37098             
37099             valid = true;
37100             return false;
37101         });
37102         
37103         if(this.allowBlank) {
37104             return true;
37105         }
37106         
37107         if(this.disabled || valid){
37108             this.markValid();
37109             return true;
37110         }
37111         
37112         this.markInvalid();
37113         return false;
37114         
37115     },
37116     
37117     markValid : function()
37118     {
37119         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37120             this.indicatorEl().removeClass('visible');
37121             this.indicatorEl().addClass('invisible');
37122         }
37123         
37124         
37125         if (Roo.bootstrap.version == 3) {
37126             this.el.removeClass([this.invalidClass, this.validClass]);
37127             this.el.addClass(this.validClass);
37128         } else {
37129             this.el.removeClass(['is-invalid','is-valid']);
37130             this.el.addClass(['is-valid']);
37131         }
37132         this.fireEvent('valid', this);
37133     },
37134     
37135     markInvalid : function(msg)
37136     {
37137         if(this.allowBlank || this.disabled){
37138             return;
37139         }
37140         
37141         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37142             this.indicatorEl().removeClass('invisible');
37143             this.indicatorEl().addClass('visible');
37144         }
37145         if (Roo.bootstrap.version == 3) {
37146             this.el.removeClass([this.invalidClass, this.validClass]);
37147             this.el.addClass(this.invalidClass);
37148         } else {
37149             this.el.removeClass(['is-invalid','is-valid']);
37150             this.el.addClass(['is-invalid']);
37151         }
37152         
37153         this.fireEvent('invalid', this, msg);
37154         
37155     },
37156     
37157     setValue : function(v, suppressEvent)
37158     {   
37159         if(this.value === v){
37160             return;
37161         }
37162         
37163         this.value = v;
37164         
37165         if(this.rendered){
37166             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37167         }
37168         
37169         Roo.each(this.radioes, function(i){
37170             i.checked = false;
37171             i.el.removeClass('checked');
37172         });
37173         
37174         Roo.each(this.radioes, function(i){
37175             
37176             if(i.value === v || i.value.toString() === v.toString()){
37177                 i.checked = true;
37178                 i.el.addClass('checked');
37179                 
37180                 if(suppressEvent !== true){
37181                     this.fireEvent('check', this, i);
37182                 }
37183                 
37184                 return false;
37185             }
37186             
37187         }, this);
37188         
37189         this.validate();
37190     },
37191     
37192     clearInvalid : function(){
37193         
37194         if(!this.el || this.preventMark){
37195             return;
37196         }
37197         
37198         this.el.removeClass([this.invalidClass]);
37199         
37200         this.fireEvent('valid', this);
37201     }
37202     
37203 });
37204
37205 Roo.apply(Roo.bootstrap.RadioSet, {
37206     
37207     groups: {},
37208     
37209     register : function(set)
37210     {
37211         this.groups[set.name] = set;
37212     },
37213     
37214     get: function(name) 
37215     {
37216         if (typeof(this.groups[name]) == 'undefined') {
37217             return false;
37218         }
37219         
37220         return this.groups[name] ;
37221     }
37222     
37223 });
37224 /*
37225  * Based on:
37226  * Ext JS Library 1.1.1
37227  * Copyright(c) 2006-2007, Ext JS, LLC.
37228  *
37229  * Originally Released Under LGPL - original licence link has changed is not relivant.
37230  *
37231  * Fork - LGPL
37232  * <script type="text/javascript">
37233  */
37234
37235
37236 /**
37237  * @class Roo.bootstrap.SplitBar
37238  * @extends Roo.util.Observable
37239  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37240  * <br><br>
37241  * Usage:
37242  * <pre><code>
37243 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37244                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37245 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37246 split.minSize = 100;
37247 split.maxSize = 600;
37248 split.animate = true;
37249 split.on('moved', splitterMoved);
37250 </code></pre>
37251  * @constructor
37252  * Create a new SplitBar
37253  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37254  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37255  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37256  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37257                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37258                         position of the SplitBar).
37259  */
37260 Roo.bootstrap.SplitBar = function(cfg){
37261     
37262     /** @private */
37263     
37264     //{
37265     //  dragElement : elm
37266     //  resizingElement: el,
37267         // optional..
37268     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37269     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37270         // existingProxy ???
37271     //}
37272     
37273     this.el = Roo.get(cfg.dragElement, true);
37274     this.el.dom.unselectable = "on";
37275     /** @private */
37276     this.resizingEl = Roo.get(cfg.resizingElement, true);
37277
37278     /**
37279      * @private
37280      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37281      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37282      * @type Number
37283      */
37284     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37285     
37286     /**
37287      * The minimum size of the resizing element. (Defaults to 0)
37288      * @type Number
37289      */
37290     this.minSize = 0;
37291     
37292     /**
37293      * The maximum size of the resizing element. (Defaults to 2000)
37294      * @type Number
37295      */
37296     this.maxSize = 2000;
37297     
37298     /**
37299      * Whether to animate the transition to the new size
37300      * @type Boolean
37301      */
37302     this.animate = false;
37303     
37304     /**
37305      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37306      * @type Boolean
37307      */
37308     this.useShim = false;
37309     
37310     /** @private */
37311     this.shim = null;
37312     
37313     if(!cfg.existingProxy){
37314         /** @private */
37315         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37316     }else{
37317         this.proxy = Roo.get(cfg.existingProxy).dom;
37318     }
37319     /** @private */
37320     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37321     
37322     /** @private */
37323     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37324     
37325     /** @private */
37326     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37327     
37328     /** @private */
37329     this.dragSpecs = {};
37330     
37331     /**
37332      * @private The adapter to use to positon and resize elements
37333      */
37334     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37335     this.adapter.init(this);
37336     
37337     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37338         /** @private */
37339         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37340         this.el.addClass("roo-splitbar-h");
37341     }else{
37342         /** @private */
37343         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37344         this.el.addClass("roo-splitbar-v");
37345     }
37346     
37347     this.addEvents({
37348         /**
37349          * @event resize
37350          * Fires when the splitter is moved (alias for {@link #event-moved})
37351          * @param {Roo.bootstrap.SplitBar} this
37352          * @param {Number} newSize the new width or height
37353          */
37354         "resize" : true,
37355         /**
37356          * @event moved
37357          * Fires when the splitter is moved
37358          * @param {Roo.bootstrap.SplitBar} this
37359          * @param {Number} newSize the new width or height
37360          */
37361         "moved" : true,
37362         /**
37363          * @event beforeresize
37364          * Fires before the splitter is dragged
37365          * @param {Roo.bootstrap.SplitBar} this
37366          */
37367         "beforeresize" : true,
37368
37369         "beforeapply" : true
37370     });
37371
37372     Roo.util.Observable.call(this);
37373 };
37374
37375 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37376     onStartProxyDrag : function(x, y){
37377         this.fireEvent("beforeresize", this);
37378         if(!this.overlay){
37379             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37380             o.unselectable();
37381             o.enableDisplayMode("block");
37382             // all splitbars share the same overlay
37383             Roo.bootstrap.SplitBar.prototype.overlay = o;
37384         }
37385         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37386         this.overlay.show();
37387         Roo.get(this.proxy).setDisplayed("block");
37388         var size = this.adapter.getElementSize(this);
37389         this.activeMinSize = this.getMinimumSize();;
37390         this.activeMaxSize = this.getMaximumSize();;
37391         var c1 = size - this.activeMinSize;
37392         var c2 = Math.max(this.activeMaxSize - size, 0);
37393         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37394             this.dd.resetConstraints();
37395             this.dd.setXConstraint(
37396                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37397                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37398             );
37399             this.dd.setYConstraint(0, 0);
37400         }else{
37401             this.dd.resetConstraints();
37402             this.dd.setXConstraint(0, 0);
37403             this.dd.setYConstraint(
37404                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37405                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37406             );
37407          }
37408         this.dragSpecs.startSize = size;
37409         this.dragSpecs.startPoint = [x, y];
37410         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37411     },
37412     
37413     /** 
37414      * @private Called after the drag operation by the DDProxy
37415      */
37416     onEndProxyDrag : function(e){
37417         Roo.get(this.proxy).setDisplayed(false);
37418         var endPoint = Roo.lib.Event.getXY(e);
37419         if(this.overlay){
37420             this.overlay.hide();
37421         }
37422         var newSize;
37423         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37424             newSize = this.dragSpecs.startSize + 
37425                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37426                     endPoint[0] - this.dragSpecs.startPoint[0] :
37427                     this.dragSpecs.startPoint[0] - endPoint[0]
37428                 );
37429         }else{
37430             newSize = this.dragSpecs.startSize + 
37431                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37432                     endPoint[1] - this.dragSpecs.startPoint[1] :
37433                     this.dragSpecs.startPoint[1] - endPoint[1]
37434                 );
37435         }
37436         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37437         if(newSize != this.dragSpecs.startSize){
37438             if(this.fireEvent('beforeapply', this, newSize) !== false){
37439                 this.adapter.setElementSize(this, newSize);
37440                 this.fireEvent("moved", this, newSize);
37441                 this.fireEvent("resize", this, newSize);
37442             }
37443         }
37444     },
37445     
37446     /**
37447      * Get the adapter this SplitBar uses
37448      * @return The adapter object
37449      */
37450     getAdapter : function(){
37451         return this.adapter;
37452     },
37453     
37454     /**
37455      * Set the adapter this SplitBar uses
37456      * @param {Object} adapter A SplitBar adapter object
37457      */
37458     setAdapter : function(adapter){
37459         this.adapter = adapter;
37460         this.adapter.init(this);
37461     },
37462     
37463     /**
37464      * Gets the minimum size for the resizing element
37465      * @return {Number} The minimum size
37466      */
37467     getMinimumSize : function(){
37468         return this.minSize;
37469     },
37470     
37471     /**
37472      * Sets the minimum size for the resizing element
37473      * @param {Number} minSize The minimum size
37474      */
37475     setMinimumSize : function(minSize){
37476         this.minSize = minSize;
37477     },
37478     
37479     /**
37480      * Gets the maximum size for the resizing element
37481      * @return {Number} The maximum size
37482      */
37483     getMaximumSize : function(){
37484         return this.maxSize;
37485     },
37486     
37487     /**
37488      * Sets the maximum size for the resizing element
37489      * @param {Number} maxSize The maximum size
37490      */
37491     setMaximumSize : function(maxSize){
37492         this.maxSize = maxSize;
37493     },
37494     
37495     /**
37496      * Sets the initialize size for the resizing element
37497      * @param {Number} size The initial size
37498      */
37499     setCurrentSize : function(size){
37500         var oldAnimate = this.animate;
37501         this.animate = false;
37502         this.adapter.setElementSize(this, size);
37503         this.animate = oldAnimate;
37504     },
37505     
37506     /**
37507      * Destroy this splitbar. 
37508      * @param {Boolean} removeEl True to remove the element
37509      */
37510     destroy : function(removeEl){
37511         if(this.shim){
37512             this.shim.remove();
37513         }
37514         this.dd.unreg();
37515         this.proxy.parentNode.removeChild(this.proxy);
37516         if(removeEl){
37517             this.el.remove();
37518         }
37519     }
37520 });
37521
37522 /**
37523  * @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.
37524  */
37525 Roo.bootstrap.SplitBar.createProxy = function(dir){
37526     var proxy = new Roo.Element(document.createElement("div"));
37527     proxy.unselectable();
37528     var cls = 'roo-splitbar-proxy';
37529     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37530     document.body.appendChild(proxy.dom);
37531     return proxy.dom;
37532 };
37533
37534 /** 
37535  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37536  * Default Adapter. It assumes the splitter and resizing element are not positioned
37537  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37538  */
37539 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37540 };
37541
37542 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37543     // do nothing for now
37544     init : function(s){
37545     
37546     },
37547     /**
37548      * Called before drag operations to get the current size of the resizing element. 
37549      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37550      */
37551      getElementSize : function(s){
37552         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37553             return s.resizingEl.getWidth();
37554         }else{
37555             return s.resizingEl.getHeight();
37556         }
37557     },
37558     
37559     /**
37560      * Called after drag operations to set the size of the resizing element.
37561      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37562      * @param {Number} newSize The new size to set
37563      * @param {Function} onComplete A function to be invoked when resizing is complete
37564      */
37565     setElementSize : function(s, newSize, onComplete){
37566         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37567             if(!s.animate){
37568                 s.resizingEl.setWidth(newSize);
37569                 if(onComplete){
37570                     onComplete(s, newSize);
37571                 }
37572             }else{
37573                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37574             }
37575         }else{
37576             
37577             if(!s.animate){
37578                 s.resizingEl.setHeight(newSize);
37579                 if(onComplete){
37580                     onComplete(s, newSize);
37581                 }
37582             }else{
37583                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37584             }
37585         }
37586     }
37587 };
37588
37589 /** 
37590  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37591  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37592  * Adapter that  moves the splitter element to align with the resized sizing element. 
37593  * Used with an absolute positioned SplitBar.
37594  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37595  * document.body, make sure you assign an id to the body element.
37596  */
37597 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37598     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37599     this.container = Roo.get(container);
37600 };
37601
37602 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37603     init : function(s){
37604         this.basic.init(s);
37605     },
37606     
37607     getElementSize : function(s){
37608         return this.basic.getElementSize(s);
37609     },
37610     
37611     setElementSize : function(s, newSize, onComplete){
37612         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37613     },
37614     
37615     moveSplitter : function(s){
37616         var yes = Roo.bootstrap.SplitBar;
37617         switch(s.placement){
37618             case yes.LEFT:
37619                 s.el.setX(s.resizingEl.getRight());
37620                 break;
37621             case yes.RIGHT:
37622                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37623                 break;
37624             case yes.TOP:
37625                 s.el.setY(s.resizingEl.getBottom());
37626                 break;
37627             case yes.BOTTOM:
37628                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37629                 break;
37630         }
37631     }
37632 };
37633
37634 /**
37635  * Orientation constant - Create a vertical SplitBar
37636  * @static
37637  * @type Number
37638  */
37639 Roo.bootstrap.SplitBar.VERTICAL = 1;
37640
37641 /**
37642  * Orientation constant - Create a horizontal SplitBar
37643  * @static
37644  * @type Number
37645  */
37646 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37647
37648 /**
37649  * Placement constant - The resizing element is to the left of the splitter element
37650  * @static
37651  * @type Number
37652  */
37653 Roo.bootstrap.SplitBar.LEFT = 1;
37654
37655 /**
37656  * Placement constant - The resizing element is to the right of the splitter element
37657  * @static
37658  * @type Number
37659  */
37660 Roo.bootstrap.SplitBar.RIGHT = 2;
37661
37662 /**
37663  * Placement constant - The resizing element is positioned above the splitter element
37664  * @static
37665  * @type Number
37666  */
37667 Roo.bootstrap.SplitBar.TOP = 3;
37668
37669 /**
37670  * Placement constant - The resizing element is positioned under splitter element
37671  * @static
37672  * @type Number
37673  */
37674 Roo.bootstrap.SplitBar.BOTTOM = 4;
37675 Roo.namespace("Roo.bootstrap.layout");/*
37676  * Based on:
37677  * Ext JS Library 1.1.1
37678  * Copyright(c) 2006-2007, Ext JS, LLC.
37679  *
37680  * Originally Released Under LGPL - original licence link has changed is not relivant.
37681  *
37682  * Fork - LGPL
37683  * <script type="text/javascript">
37684  */
37685
37686 /**
37687  * @class Roo.bootstrap.layout.Manager
37688  * @extends Roo.bootstrap.Component
37689  * Base class for layout managers.
37690  */
37691 Roo.bootstrap.layout.Manager = function(config)
37692 {
37693     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37694
37695
37696
37697
37698
37699     /** false to disable window resize monitoring @type Boolean */
37700     this.monitorWindowResize = true;
37701     this.regions = {};
37702     this.addEvents({
37703         /**
37704          * @event layout
37705          * Fires when a layout is performed.
37706          * @param {Roo.LayoutManager} this
37707          */
37708         "layout" : true,
37709         /**
37710          * @event regionresized
37711          * Fires when the user resizes a region.
37712          * @param {Roo.LayoutRegion} region The resized region
37713          * @param {Number} newSize The new size (width for east/west, height for north/south)
37714          */
37715         "regionresized" : true,
37716         /**
37717          * @event regioncollapsed
37718          * Fires when a region is collapsed.
37719          * @param {Roo.LayoutRegion} region The collapsed region
37720          */
37721         "regioncollapsed" : true,
37722         /**
37723          * @event regionexpanded
37724          * Fires when a region is expanded.
37725          * @param {Roo.LayoutRegion} region The expanded region
37726          */
37727         "regionexpanded" : true
37728     });
37729     this.updating = false;
37730
37731     if (config.el) {
37732         this.el = Roo.get(config.el);
37733         this.initEvents();
37734     }
37735
37736 };
37737
37738 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37739
37740
37741     regions : null,
37742
37743     monitorWindowResize : true,
37744
37745
37746     updating : false,
37747
37748
37749     onRender : function(ct, position)
37750     {
37751         if(!this.el){
37752             this.el = Roo.get(ct);
37753             this.initEvents();
37754         }
37755         //this.fireEvent('render',this);
37756     },
37757
37758
37759     initEvents: function()
37760     {
37761
37762
37763         // ie scrollbar fix
37764         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37765             document.body.scroll = "no";
37766         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37767             this.el.position('relative');
37768         }
37769         this.id = this.el.id;
37770         this.el.addClass("roo-layout-container");
37771         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37772         if(this.el.dom != document.body ) {
37773             this.el.on('resize', this.layout,this);
37774             this.el.on('show', this.layout,this);
37775         }
37776
37777     },
37778
37779     /**
37780      * Returns true if this layout is currently being updated
37781      * @return {Boolean}
37782      */
37783     isUpdating : function(){
37784         return this.updating;
37785     },
37786
37787     /**
37788      * Suspend the LayoutManager from doing auto-layouts while
37789      * making multiple add or remove calls
37790      */
37791     beginUpdate : function(){
37792         this.updating = true;
37793     },
37794
37795     /**
37796      * Restore auto-layouts and optionally disable the manager from performing a layout
37797      * @param {Boolean} noLayout true to disable a layout update
37798      */
37799     endUpdate : function(noLayout){
37800         this.updating = false;
37801         if(!noLayout){
37802             this.layout();
37803         }
37804     },
37805
37806     layout: function(){
37807         // abstract...
37808     },
37809
37810     onRegionResized : function(region, newSize){
37811         this.fireEvent("regionresized", region, newSize);
37812         this.layout();
37813     },
37814
37815     onRegionCollapsed : function(region){
37816         this.fireEvent("regioncollapsed", region);
37817     },
37818
37819     onRegionExpanded : function(region){
37820         this.fireEvent("regionexpanded", region);
37821     },
37822
37823     /**
37824      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37825      * performs box-model adjustments.
37826      * @return {Object} The size as an object {width: (the width), height: (the height)}
37827      */
37828     getViewSize : function()
37829     {
37830         var size;
37831         if(this.el.dom != document.body){
37832             size = this.el.getSize();
37833         }else{
37834             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37835         }
37836         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37837         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37838         return size;
37839     },
37840
37841     /**
37842      * Returns the Element this layout is bound to.
37843      * @return {Roo.Element}
37844      */
37845     getEl : function(){
37846         return this.el;
37847     },
37848
37849     /**
37850      * Returns the specified region.
37851      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37852      * @return {Roo.LayoutRegion}
37853      */
37854     getRegion : function(target){
37855         return this.regions[target.toLowerCase()];
37856     },
37857
37858     onWindowResize : function(){
37859         if(this.monitorWindowResize){
37860             this.layout();
37861         }
37862     }
37863 });
37864 /*
37865  * Based on:
37866  * Ext JS Library 1.1.1
37867  * Copyright(c) 2006-2007, Ext JS, LLC.
37868  *
37869  * Originally Released Under LGPL - original licence link has changed is not relivant.
37870  *
37871  * Fork - LGPL
37872  * <script type="text/javascript">
37873  */
37874 /**
37875  * @class Roo.bootstrap.layout.Border
37876  * @extends Roo.bootstrap.layout.Manager
37877  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37878  * please see: examples/bootstrap/nested.html<br><br>
37879  
37880 <b>The container the layout is rendered into can be either the body element or any other element.
37881 If it is not the body element, the container needs to either be an absolute positioned element,
37882 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37883 the container size if it is not the body element.</b>
37884
37885 * @constructor
37886 * Create a new Border
37887 * @param {Object} config Configuration options
37888  */
37889 Roo.bootstrap.layout.Border = function(config){
37890     config = config || {};
37891     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37892     
37893     
37894     
37895     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37896         if(config[region]){
37897             config[region].region = region;
37898             this.addRegion(config[region]);
37899         }
37900     },this);
37901     
37902 };
37903
37904 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37905
37906 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37907     
37908     parent : false, // this might point to a 'nest' or a ???
37909     
37910     /**
37911      * Creates and adds a new region if it doesn't already exist.
37912      * @param {String} target The target region key (north, south, east, west or center).
37913      * @param {Object} config The regions config object
37914      * @return {BorderLayoutRegion} The new region
37915      */
37916     addRegion : function(config)
37917     {
37918         if(!this.regions[config.region]){
37919             var r = this.factory(config);
37920             this.bindRegion(r);
37921         }
37922         return this.regions[config.region];
37923     },
37924
37925     // private (kinda)
37926     bindRegion : function(r){
37927         this.regions[r.config.region] = r;
37928         
37929         r.on("visibilitychange",    this.layout, this);
37930         r.on("paneladded",          this.layout, this);
37931         r.on("panelremoved",        this.layout, this);
37932         r.on("invalidated",         this.layout, this);
37933         r.on("resized",             this.onRegionResized, this);
37934         r.on("collapsed",           this.onRegionCollapsed, this);
37935         r.on("expanded",            this.onRegionExpanded, this);
37936     },
37937
37938     /**
37939      * Performs a layout update.
37940      */
37941     layout : function()
37942     {
37943         if(this.updating) {
37944             return;
37945         }
37946         
37947         // render all the rebions if they have not been done alreayd?
37948         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37949             if(this.regions[region] && !this.regions[region].bodyEl){
37950                 this.regions[region].onRender(this.el)
37951             }
37952         },this);
37953         
37954         var size = this.getViewSize();
37955         var w = size.width;
37956         var h = size.height;
37957         var centerW = w;
37958         var centerH = h;
37959         var centerY = 0;
37960         var centerX = 0;
37961         //var x = 0, y = 0;
37962
37963         var rs = this.regions;
37964         var north = rs["north"];
37965         var south = rs["south"]; 
37966         var west = rs["west"];
37967         var east = rs["east"];
37968         var center = rs["center"];
37969         //if(this.hideOnLayout){ // not supported anymore
37970             //c.el.setStyle("display", "none");
37971         //}
37972         if(north && north.isVisible()){
37973             var b = north.getBox();
37974             var m = north.getMargins();
37975             b.width = w - (m.left+m.right);
37976             b.x = m.left;
37977             b.y = m.top;
37978             centerY = b.height + b.y + m.bottom;
37979             centerH -= centerY;
37980             north.updateBox(this.safeBox(b));
37981         }
37982         if(south && south.isVisible()){
37983             var b = south.getBox();
37984             var m = south.getMargins();
37985             b.width = w - (m.left+m.right);
37986             b.x = m.left;
37987             var totalHeight = (b.height + m.top + m.bottom);
37988             b.y = h - totalHeight + m.top;
37989             centerH -= totalHeight;
37990             south.updateBox(this.safeBox(b));
37991         }
37992         if(west && west.isVisible()){
37993             var b = west.getBox();
37994             var m = west.getMargins();
37995             b.height = centerH - (m.top+m.bottom);
37996             b.x = m.left;
37997             b.y = centerY + m.top;
37998             var totalWidth = (b.width + m.left + m.right);
37999             centerX += totalWidth;
38000             centerW -= totalWidth;
38001             west.updateBox(this.safeBox(b));
38002         }
38003         if(east && east.isVisible()){
38004             var b = east.getBox();
38005             var m = east.getMargins();
38006             b.height = centerH - (m.top+m.bottom);
38007             var totalWidth = (b.width + m.left + m.right);
38008             b.x = w - totalWidth + m.left;
38009             b.y = centerY + m.top;
38010             centerW -= totalWidth;
38011             east.updateBox(this.safeBox(b));
38012         }
38013         if(center){
38014             var m = center.getMargins();
38015             var centerBox = {
38016                 x: centerX + m.left,
38017                 y: centerY + m.top,
38018                 width: centerW - (m.left+m.right),
38019                 height: centerH - (m.top+m.bottom)
38020             };
38021             //if(this.hideOnLayout){
38022                 //center.el.setStyle("display", "block");
38023             //}
38024             center.updateBox(this.safeBox(centerBox));
38025         }
38026         this.el.repaint();
38027         this.fireEvent("layout", this);
38028     },
38029
38030     // private
38031     safeBox : function(box){
38032         box.width = Math.max(0, box.width);
38033         box.height = Math.max(0, box.height);
38034         return box;
38035     },
38036
38037     /**
38038      * Adds a ContentPanel (or subclass) to this layout.
38039      * @param {String} target The target region key (north, south, east, west or center).
38040      * @param {Roo.ContentPanel} panel The panel to add
38041      * @return {Roo.ContentPanel} The added panel
38042      */
38043     add : function(target, panel){
38044          
38045         target = target.toLowerCase();
38046         return this.regions[target].add(panel);
38047     },
38048
38049     /**
38050      * Remove a ContentPanel (or subclass) to this layout.
38051      * @param {String} target The target region key (north, south, east, west or center).
38052      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38053      * @return {Roo.ContentPanel} The removed panel
38054      */
38055     remove : function(target, panel){
38056         target = target.toLowerCase();
38057         return this.regions[target].remove(panel);
38058     },
38059
38060     /**
38061      * Searches all regions for a panel with the specified id
38062      * @param {String} panelId
38063      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38064      */
38065     findPanel : function(panelId){
38066         var rs = this.regions;
38067         for(var target in rs){
38068             if(typeof rs[target] != "function"){
38069                 var p = rs[target].getPanel(panelId);
38070                 if(p){
38071                     return p;
38072                 }
38073             }
38074         }
38075         return null;
38076     },
38077
38078     /**
38079      * Searches all regions for a panel with the specified id and activates (shows) it.
38080      * @param {String/ContentPanel} panelId The panels id or the panel itself
38081      * @return {Roo.ContentPanel} The shown panel or null
38082      */
38083     showPanel : function(panelId) {
38084       var rs = this.regions;
38085       for(var target in rs){
38086          var r = rs[target];
38087          if(typeof r != "function"){
38088             if(r.hasPanel(panelId)){
38089                return r.showPanel(panelId);
38090             }
38091          }
38092       }
38093       return null;
38094    },
38095
38096    /**
38097      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38098      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38099      */
38100    /*
38101     restoreState : function(provider){
38102         if(!provider){
38103             provider = Roo.state.Manager;
38104         }
38105         var sm = new Roo.LayoutStateManager();
38106         sm.init(this, provider);
38107     },
38108 */
38109  
38110  
38111     /**
38112      * Adds a xtype elements to the layout.
38113      * <pre><code>
38114
38115 layout.addxtype({
38116        xtype : 'ContentPanel',
38117        region: 'west',
38118        items: [ .... ]
38119    }
38120 );
38121
38122 layout.addxtype({
38123         xtype : 'NestedLayoutPanel',
38124         region: 'west',
38125         layout: {
38126            center: { },
38127            west: { }   
38128         },
38129         items : [ ... list of content panels or nested layout panels.. ]
38130    }
38131 );
38132 </code></pre>
38133      * @param {Object} cfg Xtype definition of item to add.
38134      */
38135     addxtype : function(cfg)
38136     {
38137         // basically accepts a pannel...
38138         // can accept a layout region..!?!?
38139         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38140         
38141         
38142         // theory?  children can only be panels??
38143         
38144         //if (!cfg.xtype.match(/Panel$/)) {
38145         //    return false;
38146         //}
38147         var ret = false;
38148         
38149         if (typeof(cfg.region) == 'undefined') {
38150             Roo.log("Failed to add Panel, region was not set");
38151             Roo.log(cfg);
38152             return false;
38153         }
38154         var region = cfg.region;
38155         delete cfg.region;
38156         
38157           
38158         var xitems = [];
38159         if (cfg.items) {
38160             xitems = cfg.items;
38161             delete cfg.items;
38162         }
38163         var nb = false;
38164         
38165         if ( region == 'center') {
38166             Roo.log("Center: " + cfg.title);
38167         }
38168         
38169         
38170         switch(cfg.xtype) 
38171         {
38172             case 'Content':  // ContentPanel (el, cfg)
38173             case 'Scroll':  // ContentPanel (el, cfg)
38174             case 'View': 
38175                 cfg.autoCreate = cfg.autoCreate || true;
38176                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38177                 //} else {
38178                 //    var el = this.el.createChild();
38179                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38180                 //}
38181                 
38182                 this.add(region, ret);
38183                 break;
38184             
38185             /*
38186             case 'TreePanel': // our new panel!
38187                 cfg.el = this.el.createChild();
38188                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38189                 this.add(region, ret);
38190                 break;
38191             */
38192             
38193             case 'Nest': 
38194                 // create a new Layout (which is  a Border Layout...
38195                 
38196                 var clayout = cfg.layout;
38197                 clayout.el  = this.el.createChild();
38198                 clayout.items   = clayout.items  || [];
38199                 
38200                 delete cfg.layout;
38201                 
38202                 // replace this exitems with the clayout ones..
38203                 xitems = clayout.items;
38204                  
38205                 // force background off if it's in center...
38206                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38207                     cfg.background = false;
38208                 }
38209                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38210                 
38211                 
38212                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38213                 //console.log('adding nested layout panel '  + cfg.toSource());
38214                 this.add(region, ret);
38215                 nb = {}; /// find first...
38216                 break;
38217             
38218             case 'Grid':
38219                 
38220                 // needs grid and region
38221                 
38222                 //var el = this.getRegion(region).el.createChild();
38223                 /*
38224                  *var el = this.el.createChild();
38225                 // create the grid first...
38226                 cfg.grid.container = el;
38227                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38228                 */
38229                 
38230                 if (region == 'center' && this.active ) {
38231                     cfg.background = false;
38232                 }
38233                 
38234                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38235                 
38236                 this.add(region, ret);
38237                 /*
38238                 if (cfg.background) {
38239                     // render grid on panel activation (if panel background)
38240                     ret.on('activate', function(gp) {
38241                         if (!gp.grid.rendered) {
38242                     //        gp.grid.render(el);
38243                         }
38244                     });
38245                 } else {
38246                   //  cfg.grid.render(el);
38247                 }
38248                 */
38249                 break;
38250            
38251            
38252             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38253                 // it was the old xcomponent building that caused this before.
38254                 // espeically if border is the top element in the tree.
38255                 ret = this;
38256                 break; 
38257                 
38258                     
38259                 
38260                 
38261                 
38262             default:
38263                 /*
38264                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38265                     
38266                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38267                     this.add(region, ret);
38268                 } else {
38269                 */
38270                     Roo.log(cfg);
38271                     throw "Can not add '" + cfg.xtype + "' to Border";
38272                     return null;
38273              
38274                                 
38275              
38276         }
38277         this.beginUpdate();
38278         // add children..
38279         var region = '';
38280         var abn = {};
38281         Roo.each(xitems, function(i)  {
38282             region = nb && i.region ? i.region : false;
38283             
38284             var add = ret.addxtype(i);
38285            
38286             if (region) {
38287                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38288                 if (!i.background) {
38289                     abn[region] = nb[region] ;
38290                 }
38291             }
38292             
38293         });
38294         this.endUpdate();
38295
38296         // make the last non-background panel active..
38297         //if (nb) { Roo.log(abn); }
38298         if (nb) {
38299             
38300             for(var r in abn) {
38301                 region = this.getRegion(r);
38302                 if (region) {
38303                     // tried using nb[r], but it does not work..
38304                      
38305                     region.showPanel(abn[r]);
38306                    
38307                 }
38308             }
38309         }
38310         return ret;
38311         
38312     },
38313     
38314     
38315 // private
38316     factory : function(cfg)
38317     {
38318         
38319         var validRegions = Roo.bootstrap.layout.Border.regions;
38320
38321         var target = cfg.region;
38322         cfg.mgr = this;
38323         
38324         var r = Roo.bootstrap.layout;
38325         Roo.log(target);
38326         switch(target){
38327             case "north":
38328                 return new r.North(cfg);
38329             case "south":
38330                 return new r.South(cfg);
38331             case "east":
38332                 return new r.East(cfg);
38333             case "west":
38334                 return new r.West(cfg);
38335             case "center":
38336                 return new r.Center(cfg);
38337         }
38338         throw 'Layout region "'+target+'" not supported.';
38339     }
38340     
38341     
38342 });
38343  /*
38344  * Based on:
38345  * Ext JS Library 1.1.1
38346  * Copyright(c) 2006-2007, Ext JS, LLC.
38347  *
38348  * Originally Released Under LGPL - original licence link has changed is not relivant.
38349  *
38350  * Fork - LGPL
38351  * <script type="text/javascript">
38352  */
38353  
38354 /**
38355  * @class Roo.bootstrap.layout.Basic
38356  * @extends Roo.util.Observable
38357  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38358  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38359  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38360  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38361  * @cfg {string}   region  the region that it inhabits..
38362  * @cfg {bool}   skipConfig skip config?
38363  * 
38364
38365  */
38366 Roo.bootstrap.layout.Basic = function(config){
38367     
38368     this.mgr = config.mgr;
38369     
38370     this.position = config.region;
38371     
38372     var skipConfig = config.skipConfig;
38373     
38374     this.events = {
38375         /**
38376          * @scope Roo.BasicLayoutRegion
38377          */
38378         
38379         /**
38380          * @event beforeremove
38381          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38382          * @param {Roo.LayoutRegion} this
38383          * @param {Roo.ContentPanel} panel The panel
38384          * @param {Object} e The cancel event object
38385          */
38386         "beforeremove" : true,
38387         /**
38388          * @event invalidated
38389          * Fires when the layout for this region is changed.
38390          * @param {Roo.LayoutRegion} this
38391          */
38392         "invalidated" : true,
38393         /**
38394          * @event visibilitychange
38395          * Fires when this region is shown or hidden 
38396          * @param {Roo.LayoutRegion} this
38397          * @param {Boolean} visibility true or false
38398          */
38399         "visibilitychange" : true,
38400         /**
38401          * @event paneladded
38402          * Fires when a panel is added. 
38403          * @param {Roo.LayoutRegion} this
38404          * @param {Roo.ContentPanel} panel The panel
38405          */
38406         "paneladded" : true,
38407         /**
38408          * @event panelremoved
38409          * Fires when a panel is removed. 
38410          * @param {Roo.LayoutRegion} this
38411          * @param {Roo.ContentPanel} panel The panel
38412          */
38413         "panelremoved" : true,
38414         /**
38415          * @event beforecollapse
38416          * Fires when this region before collapse.
38417          * @param {Roo.LayoutRegion} this
38418          */
38419         "beforecollapse" : true,
38420         /**
38421          * @event collapsed
38422          * Fires when this region is collapsed.
38423          * @param {Roo.LayoutRegion} this
38424          */
38425         "collapsed" : true,
38426         /**
38427          * @event expanded
38428          * Fires when this region is expanded.
38429          * @param {Roo.LayoutRegion} this
38430          */
38431         "expanded" : true,
38432         /**
38433          * @event slideshow
38434          * Fires when this region is slid into view.
38435          * @param {Roo.LayoutRegion} this
38436          */
38437         "slideshow" : true,
38438         /**
38439          * @event slidehide
38440          * Fires when this region slides out of view. 
38441          * @param {Roo.LayoutRegion} this
38442          */
38443         "slidehide" : true,
38444         /**
38445          * @event panelactivated
38446          * Fires when a panel is activated. 
38447          * @param {Roo.LayoutRegion} this
38448          * @param {Roo.ContentPanel} panel The activated panel
38449          */
38450         "panelactivated" : true,
38451         /**
38452          * @event resized
38453          * Fires when the user resizes this region. 
38454          * @param {Roo.LayoutRegion} this
38455          * @param {Number} newSize The new size (width for east/west, height for north/south)
38456          */
38457         "resized" : true
38458     };
38459     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38460     this.panels = new Roo.util.MixedCollection();
38461     this.panels.getKey = this.getPanelId.createDelegate(this);
38462     this.box = null;
38463     this.activePanel = null;
38464     // ensure listeners are added...
38465     
38466     if (config.listeners || config.events) {
38467         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38468             listeners : config.listeners || {},
38469             events : config.events || {}
38470         });
38471     }
38472     
38473     if(skipConfig !== true){
38474         this.applyConfig(config);
38475     }
38476 };
38477
38478 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38479 {
38480     getPanelId : function(p){
38481         return p.getId();
38482     },
38483     
38484     applyConfig : function(config){
38485         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38486         this.config = config;
38487         
38488     },
38489     
38490     /**
38491      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38492      * the width, for horizontal (north, south) the height.
38493      * @param {Number} newSize The new width or height
38494      */
38495     resizeTo : function(newSize){
38496         var el = this.el ? this.el :
38497                  (this.activePanel ? this.activePanel.getEl() : null);
38498         if(el){
38499             switch(this.position){
38500                 case "east":
38501                 case "west":
38502                     el.setWidth(newSize);
38503                     this.fireEvent("resized", this, newSize);
38504                 break;
38505                 case "north":
38506                 case "south":
38507                     el.setHeight(newSize);
38508                     this.fireEvent("resized", this, newSize);
38509                 break;                
38510             }
38511         }
38512     },
38513     
38514     getBox : function(){
38515         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38516     },
38517     
38518     getMargins : function(){
38519         return this.margins;
38520     },
38521     
38522     updateBox : function(box){
38523         this.box = box;
38524         var el = this.activePanel.getEl();
38525         el.dom.style.left = box.x + "px";
38526         el.dom.style.top = box.y + "px";
38527         this.activePanel.setSize(box.width, box.height);
38528     },
38529     
38530     /**
38531      * Returns the container element for this region.
38532      * @return {Roo.Element}
38533      */
38534     getEl : function(){
38535         return this.activePanel;
38536     },
38537     
38538     /**
38539      * Returns true if this region is currently visible.
38540      * @return {Boolean}
38541      */
38542     isVisible : function(){
38543         return this.activePanel ? true : false;
38544     },
38545     
38546     setActivePanel : function(panel){
38547         panel = this.getPanel(panel);
38548         if(this.activePanel && this.activePanel != panel){
38549             this.activePanel.setActiveState(false);
38550             this.activePanel.getEl().setLeftTop(-10000,-10000);
38551         }
38552         this.activePanel = panel;
38553         panel.setActiveState(true);
38554         if(this.box){
38555             panel.setSize(this.box.width, this.box.height);
38556         }
38557         this.fireEvent("panelactivated", this, panel);
38558         this.fireEvent("invalidated");
38559     },
38560     
38561     /**
38562      * Show the specified panel.
38563      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38564      * @return {Roo.ContentPanel} The shown panel or null
38565      */
38566     showPanel : function(panel){
38567         panel = this.getPanel(panel);
38568         if(panel){
38569             this.setActivePanel(panel);
38570         }
38571         return panel;
38572     },
38573     
38574     /**
38575      * Get the active panel for this region.
38576      * @return {Roo.ContentPanel} The active panel or null
38577      */
38578     getActivePanel : function(){
38579         return this.activePanel;
38580     },
38581     
38582     /**
38583      * Add the passed ContentPanel(s)
38584      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38585      * @return {Roo.ContentPanel} The panel added (if only one was added)
38586      */
38587     add : function(panel){
38588         if(arguments.length > 1){
38589             for(var i = 0, len = arguments.length; i < len; i++) {
38590                 this.add(arguments[i]);
38591             }
38592             return null;
38593         }
38594         if(this.hasPanel(panel)){
38595             this.showPanel(panel);
38596             return panel;
38597         }
38598         var el = panel.getEl();
38599         if(el.dom.parentNode != this.mgr.el.dom){
38600             this.mgr.el.dom.appendChild(el.dom);
38601         }
38602         if(panel.setRegion){
38603             panel.setRegion(this);
38604         }
38605         this.panels.add(panel);
38606         el.setStyle("position", "absolute");
38607         if(!panel.background){
38608             this.setActivePanel(panel);
38609             if(this.config.initialSize && this.panels.getCount()==1){
38610                 this.resizeTo(this.config.initialSize);
38611             }
38612         }
38613         this.fireEvent("paneladded", this, panel);
38614         return panel;
38615     },
38616     
38617     /**
38618      * Returns true if the panel is in this region.
38619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38620      * @return {Boolean}
38621      */
38622     hasPanel : function(panel){
38623         if(typeof panel == "object"){ // must be panel obj
38624             panel = panel.getId();
38625         }
38626         return this.getPanel(panel) ? true : false;
38627     },
38628     
38629     /**
38630      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38631      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38632      * @param {Boolean} preservePanel Overrides the config preservePanel option
38633      * @return {Roo.ContentPanel} The panel that was removed
38634      */
38635     remove : function(panel, preservePanel){
38636         panel = this.getPanel(panel);
38637         if(!panel){
38638             return null;
38639         }
38640         var e = {};
38641         this.fireEvent("beforeremove", this, panel, e);
38642         if(e.cancel === true){
38643             return null;
38644         }
38645         var panelId = panel.getId();
38646         this.panels.removeKey(panelId);
38647         return panel;
38648     },
38649     
38650     /**
38651      * Returns the panel specified or null if it's not in this region.
38652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38653      * @return {Roo.ContentPanel}
38654      */
38655     getPanel : function(id){
38656         if(typeof id == "object"){ // must be panel obj
38657             return id;
38658         }
38659         return this.panels.get(id);
38660     },
38661     
38662     /**
38663      * Returns this regions position (north/south/east/west/center).
38664      * @return {String} 
38665      */
38666     getPosition: function(){
38667         return this.position;    
38668     }
38669 });/*
38670  * Based on:
38671  * Ext JS Library 1.1.1
38672  * Copyright(c) 2006-2007, Ext JS, LLC.
38673  *
38674  * Originally Released Under LGPL - original licence link has changed is not relivant.
38675  *
38676  * Fork - LGPL
38677  * <script type="text/javascript">
38678  */
38679  
38680 /**
38681  * @class Roo.bootstrap.layout.Region
38682  * @extends Roo.bootstrap.layout.Basic
38683  * This class represents a region in a layout manager.
38684  
38685  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38686  * @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})
38687  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38688  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38689  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38690  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38691  * @cfg {String}    title           The title for the region (overrides panel titles)
38692  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38693  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38694  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38695  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38696  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38697  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38698  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38699  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38700  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38701  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38702
38703  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38704  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38705  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38706  * @cfg {Number}    width           For East/West panels
38707  * @cfg {Number}    height          For North/South panels
38708  * @cfg {Boolean}   split           To show the splitter
38709  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38710  * 
38711  * @cfg {string}   cls             Extra CSS classes to add to region
38712  * 
38713  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38714  * @cfg {string}   region  the region that it inhabits..
38715  *
38716
38717  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38718  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38719
38720  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38721  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38722  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38723  */
38724 Roo.bootstrap.layout.Region = function(config)
38725 {
38726     this.applyConfig(config);
38727
38728     var mgr = config.mgr;
38729     var pos = config.region;
38730     config.skipConfig = true;
38731     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38732     
38733     if (mgr.el) {
38734         this.onRender(mgr.el);   
38735     }
38736      
38737     this.visible = true;
38738     this.collapsed = false;
38739     this.unrendered_panels = [];
38740 };
38741
38742 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38743
38744     position: '', // set by wrapper (eg. north/south etc..)
38745     unrendered_panels : null,  // unrendered panels.
38746     
38747     tabPosition : false,
38748     
38749     mgr: false, // points to 'Border'
38750     
38751     
38752     createBody : function(){
38753         /** This region's body element 
38754         * @type Roo.Element */
38755         this.bodyEl = this.el.createChild({
38756                 tag: "div",
38757                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38758         });
38759     },
38760
38761     onRender: function(ctr, pos)
38762     {
38763         var dh = Roo.DomHelper;
38764         /** This region's container element 
38765         * @type Roo.Element */
38766         this.el = dh.append(ctr.dom, {
38767                 tag: "div",
38768                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38769             }, true);
38770         /** This region's title element 
38771         * @type Roo.Element */
38772     
38773         this.titleEl = dh.append(this.el.dom,  {
38774                 tag: "div",
38775                 unselectable: "on",
38776                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38777                 children:[
38778                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38779                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38780                 ]
38781             }, true);
38782         
38783         this.titleEl.enableDisplayMode();
38784         /** This region's title text element 
38785         * @type HTMLElement */
38786         this.titleTextEl = this.titleEl.dom.firstChild;
38787         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38788         /*
38789         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38790         this.closeBtn.enableDisplayMode();
38791         this.closeBtn.on("click", this.closeClicked, this);
38792         this.closeBtn.hide();
38793     */
38794         this.createBody(this.config);
38795         if(this.config.hideWhenEmpty){
38796             this.hide();
38797             this.on("paneladded", this.validateVisibility, this);
38798             this.on("panelremoved", this.validateVisibility, this);
38799         }
38800         if(this.autoScroll){
38801             this.bodyEl.setStyle("overflow", "auto");
38802         }else{
38803             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38804         }
38805         //if(c.titlebar !== false){
38806             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38807                 this.titleEl.hide();
38808             }else{
38809                 this.titleEl.show();
38810                 if(this.config.title){
38811                     this.titleTextEl.innerHTML = this.config.title;
38812                 }
38813             }
38814         //}
38815         if(this.config.collapsed){
38816             this.collapse(true);
38817         }
38818         if(this.config.hidden){
38819             this.hide();
38820         }
38821         
38822         if (this.unrendered_panels && this.unrendered_panels.length) {
38823             for (var i =0;i< this.unrendered_panels.length; i++) {
38824                 this.add(this.unrendered_panels[i]);
38825             }
38826             this.unrendered_panels = null;
38827             
38828         }
38829         
38830     },
38831     
38832     applyConfig : function(c)
38833     {
38834         /*
38835          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38836             var dh = Roo.DomHelper;
38837             if(c.titlebar !== false){
38838                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38839                 this.collapseBtn.on("click", this.collapse, this);
38840                 this.collapseBtn.enableDisplayMode();
38841                 /*
38842                 if(c.showPin === true || this.showPin){
38843                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38844                     this.stickBtn.enableDisplayMode();
38845                     this.stickBtn.on("click", this.expand, this);
38846                     this.stickBtn.hide();
38847                 }
38848                 
38849             }
38850             */
38851             /** This region's collapsed element
38852             * @type Roo.Element */
38853             /*
38854              *
38855             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38856                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38857             ]}, true);
38858             
38859             if(c.floatable !== false){
38860                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38861                this.collapsedEl.on("click", this.collapseClick, this);
38862             }
38863
38864             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38865                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38866                    id: "message", unselectable: "on", style:{"float":"left"}});
38867                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38868              }
38869             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38870             this.expandBtn.on("click", this.expand, this);
38871             
38872         }
38873         
38874         if(this.collapseBtn){
38875             this.collapseBtn.setVisible(c.collapsible == true);
38876         }
38877         
38878         this.cmargins = c.cmargins || this.cmargins ||
38879                          (this.position == "west" || this.position == "east" ?
38880                              {top: 0, left: 2, right:2, bottom: 0} :
38881                              {top: 2, left: 0, right:0, bottom: 2});
38882         */
38883         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38884         
38885         
38886         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38887         
38888         this.autoScroll = c.autoScroll || false;
38889         
38890         
38891        
38892         
38893         this.duration = c.duration || .30;
38894         this.slideDuration = c.slideDuration || .45;
38895         this.config = c;
38896        
38897     },
38898     /**
38899      * Returns true if this region is currently visible.
38900      * @return {Boolean}
38901      */
38902     isVisible : function(){
38903         return this.visible;
38904     },
38905
38906     /**
38907      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38908      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38909      */
38910     //setCollapsedTitle : function(title){
38911     //    title = title || "&#160;";
38912      //   if(this.collapsedTitleTextEl){
38913       //      this.collapsedTitleTextEl.innerHTML = title;
38914        // }
38915     //},
38916
38917     getBox : function(){
38918         var b;
38919       //  if(!this.collapsed){
38920             b = this.el.getBox(false, true);
38921        // }else{
38922           //  b = this.collapsedEl.getBox(false, true);
38923         //}
38924         return b;
38925     },
38926
38927     getMargins : function(){
38928         return this.margins;
38929         //return this.collapsed ? this.cmargins : this.margins;
38930     },
38931 /*
38932     highlight : function(){
38933         this.el.addClass("x-layout-panel-dragover");
38934     },
38935
38936     unhighlight : function(){
38937         this.el.removeClass("x-layout-panel-dragover");
38938     },
38939 */
38940     updateBox : function(box)
38941     {
38942         if (!this.bodyEl) {
38943             return; // not rendered yet..
38944         }
38945         
38946         this.box = box;
38947         if(!this.collapsed){
38948             this.el.dom.style.left = box.x + "px";
38949             this.el.dom.style.top = box.y + "px";
38950             this.updateBody(box.width, box.height);
38951         }else{
38952             this.collapsedEl.dom.style.left = box.x + "px";
38953             this.collapsedEl.dom.style.top = box.y + "px";
38954             this.collapsedEl.setSize(box.width, box.height);
38955         }
38956         if(this.tabs){
38957             this.tabs.autoSizeTabs();
38958         }
38959     },
38960
38961     updateBody : function(w, h)
38962     {
38963         if(w !== null){
38964             this.el.setWidth(w);
38965             w -= this.el.getBorderWidth("rl");
38966             if(this.config.adjustments){
38967                 w += this.config.adjustments[0];
38968             }
38969         }
38970         if(h !== null && h > 0){
38971             this.el.setHeight(h);
38972             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38973             h -= this.el.getBorderWidth("tb");
38974             if(this.config.adjustments){
38975                 h += this.config.adjustments[1];
38976             }
38977             this.bodyEl.setHeight(h);
38978             if(this.tabs){
38979                 h = this.tabs.syncHeight(h);
38980             }
38981         }
38982         if(this.panelSize){
38983             w = w !== null ? w : this.panelSize.width;
38984             h = h !== null ? h : this.panelSize.height;
38985         }
38986         if(this.activePanel){
38987             var el = this.activePanel.getEl();
38988             w = w !== null ? w : el.getWidth();
38989             h = h !== null ? h : el.getHeight();
38990             this.panelSize = {width: w, height: h};
38991             this.activePanel.setSize(w, h);
38992         }
38993         if(Roo.isIE && this.tabs){
38994             this.tabs.el.repaint();
38995         }
38996     },
38997
38998     /**
38999      * Returns the container element for this region.
39000      * @return {Roo.Element}
39001      */
39002     getEl : function(){
39003         return this.el;
39004     },
39005
39006     /**
39007      * Hides this region.
39008      */
39009     hide : function(){
39010         //if(!this.collapsed){
39011             this.el.dom.style.left = "-2000px";
39012             this.el.hide();
39013         //}else{
39014          //   this.collapsedEl.dom.style.left = "-2000px";
39015          //   this.collapsedEl.hide();
39016        // }
39017         this.visible = false;
39018         this.fireEvent("visibilitychange", this, false);
39019     },
39020
39021     /**
39022      * Shows this region if it was previously hidden.
39023      */
39024     show : function(){
39025         //if(!this.collapsed){
39026             this.el.show();
39027         //}else{
39028         //    this.collapsedEl.show();
39029        // }
39030         this.visible = true;
39031         this.fireEvent("visibilitychange", this, true);
39032     },
39033 /*
39034     closeClicked : function(){
39035         if(this.activePanel){
39036             this.remove(this.activePanel);
39037         }
39038     },
39039
39040     collapseClick : function(e){
39041         if(this.isSlid){
39042            e.stopPropagation();
39043            this.slideIn();
39044         }else{
39045            e.stopPropagation();
39046            this.slideOut();
39047         }
39048     },
39049 */
39050     /**
39051      * Collapses this region.
39052      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39053      */
39054     /*
39055     collapse : function(skipAnim, skipCheck = false){
39056         if(this.collapsed) {
39057             return;
39058         }
39059         
39060         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39061             
39062             this.collapsed = true;
39063             if(this.split){
39064                 this.split.el.hide();
39065             }
39066             if(this.config.animate && skipAnim !== true){
39067                 this.fireEvent("invalidated", this);
39068                 this.animateCollapse();
39069             }else{
39070                 this.el.setLocation(-20000,-20000);
39071                 this.el.hide();
39072                 this.collapsedEl.show();
39073                 this.fireEvent("collapsed", this);
39074                 this.fireEvent("invalidated", this);
39075             }
39076         }
39077         
39078     },
39079 */
39080     animateCollapse : function(){
39081         // overridden
39082     },
39083
39084     /**
39085      * Expands this region if it was previously collapsed.
39086      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39087      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39088      */
39089     /*
39090     expand : function(e, skipAnim){
39091         if(e) {
39092             e.stopPropagation();
39093         }
39094         if(!this.collapsed || this.el.hasActiveFx()) {
39095             return;
39096         }
39097         if(this.isSlid){
39098             this.afterSlideIn();
39099             skipAnim = true;
39100         }
39101         this.collapsed = false;
39102         if(this.config.animate && skipAnim !== true){
39103             this.animateExpand();
39104         }else{
39105             this.el.show();
39106             if(this.split){
39107                 this.split.el.show();
39108             }
39109             this.collapsedEl.setLocation(-2000,-2000);
39110             this.collapsedEl.hide();
39111             this.fireEvent("invalidated", this);
39112             this.fireEvent("expanded", this);
39113         }
39114     },
39115 */
39116     animateExpand : function(){
39117         // overridden
39118     },
39119
39120     initTabs : function()
39121     {
39122         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39123         
39124         var ts = new Roo.bootstrap.panel.Tabs({
39125             el: this.bodyEl.dom,
39126             region : this,
39127             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39128             disableTooltips: this.config.disableTabTips,
39129             toolbar : this.config.toolbar
39130         });
39131         
39132         if(this.config.hideTabs){
39133             ts.stripWrap.setDisplayed(false);
39134         }
39135         this.tabs = ts;
39136         ts.resizeTabs = this.config.resizeTabs === true;
39137         ts.minTabWidth = this.config.minTabWidth || 40;
39138         ts.maxTabWidth = this.config.maxTabWidth || 250;
39139         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39140         ts.monitorResize = false;
39141         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39142         ts.bodyEl.addClass('roo-layout-tabs-body');
39143         this.panels.each(this.initPanelAsTab, this);
39144     },
39145
39146     initPanelAsTab : function(panel){
39147         var ti = this.tabs.addTab(
39148             panel.getEl().id,
39149             panel.getTitle(),
39150             null,
39151             this.config.closeOnTab && panel.isClosable(),
39152             panel.tpl
39153         );
39154         if(panel.tabTip !== undefined){
39155             ti.setTooltip(panel.tabTip);
39156         }
39157         ti.on("activate", function(){
39158               this.setActivePanel(panel);
39159         }, this);
39160         
39161         if(this.config.closeOnTab){
39162             ti.on("beforeclose", function(t, e){
39163                 e.cancel = true;
39164                 this.remove(panel);
39165             }, this);
39166         }
39167         
39168         panel.tabItem = ti;
39169         
39170         return ti;
39171     },
39172
39173     updatePanelTitle : function(panel, title)
39174     {
39175         if(this.activePanel == panel){
39176             this.updateTitle(title);
39177         }
39178         if(this.tabs){
39179             var ti = this.tabs.getTab(panel.getEl().id);
39180             ti.setText(title);
39181             if(panel.tabTip !== undefined){
39182                 ti.setTooltip(panel.tabTip);
39183             }
39184         }
39185     },
39186
39187     updateTitle : function(title){
39188         if(this.titleTextEl && !this.config.title){
39189             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39190         }
39191     },
39192
39193     setActivePanel : function(panel)
39194     {
39195         panel = this.getPanel(panel);
39196         if(this.activePanel && this.activePanel != panel){
39197             if(this.activePanel.setActiveState(false) === false){
39198                 return;
39199             }
39200         }
39201         this.activePanel = panel;
39202         panel.setActiveState(true);
39203         if(this.panelSize){
39204             panel.setSize(this.panelSize.width, this.panelSize.height);
39205         }
39206         if(this.closeBtn){
39207             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39208         }
39209         this.updateTitle(panel.getTitle());
39210         if(this.tabs){
39211             this.fireEvent("invalidated", this);
39212         }
39213         this.fireEvent("panelactivated", this, panel);
39214     },
39215
39216     /**
39217      * Shows the specified panel.
39218      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39219      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39220      */
39221     showPanel : function(panel)
39222     {
39223         panel = this.getPanel(panel);
39224         if(panel){
39225             if(this.tabs){
39226                 var tab = this.tabs.getTab(panel.getEl().id);
39227                 if(tab.isHidden()){
39228                     this.tabs.unhideTab(tab.id);
39229                 }
39230                 tab.activate();
39231             }else{
39232                 this.setActivePanel(panel);
39233             }
39234         }
39235         return panel;
39236     },
39237
39238     /**
39239      * Get the active panel for this region.
39240      * @return {Roo.ContentPanel} The active panel or null
39241      */
39242     getActivePanel : function(){
39243         return this.activePanel;
39244     },
39245
39246     validateVisibility : function(){
39247         if(this.panels.getCount() < 1){
39248             this.updateTitle("&#160;");
39249             this.closeBtn.hide();
39250             this.hide();
39251         }else{
39252             if(!this.isVisible()){
39253                 this.show();
39254             }
39255         }
39256     },
39257
39258     /**
39259      * Adds the passed ContentPanel(s) to this region.
39260      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39261      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39262      */
39263     add : function(panel)
39264     {
39265         if(arguments.length > 1){
39266             for(var i = 0, len = arguments.length; i < len; i++) {
39267                 this.add(arguments[i]);
39268             }
39269             return null;
39270         }
39271         
39272         // if we have not been rendered yet, then we can not really do much of this..
39273         if (!this.bodyEl) {
39274             this.unrendered_panels.push(panel);
39275             return panel;
39276         }
39277         
39278         
39279         
39280         
39281         if(this.hasPanel(panel)){
39282             this.showPanel(panel);
39283             return panel;
39284         }
39285         panel.setRegion(this);
39286         this.panels.add(panel);
39287        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39288             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39289             // and hide them... ???
39290             this.bodyEl.dom.appendChild(panel.getEl().dom);
39291             if(panel.background !== true){
39292                 this.setActivePanel(panel);
39293             }
39294             this.fireEvent("paneladded", this, panel);
39295             return panel;
39296         }
39297         */
39298         if(!this.tabs){
39299             this.initTabs();
39300         }else{
39301             this.initPanelAsTab(panel);
39302         }
39303         
39304         
39305         if(panel.background !== true){
39306             this.tabs.activate(panel.getEl().id);
39307         }
39308         this.fireEvent("paneladded", this, panel);
39309         return panel;
39310     },
39311
39312     /**
39313      * Hides the tab for the specified panel.
39314      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39315      */
39316     hidePanel : function(panel){
39317         if(this.tabs && (panel = this.getPanel(panel))){
39318             this.tabs.hideTab(panel.getEl().id);
39319         }
39320     },
39321
39322     /**
39323      * Unhides the tab for a previously hidden panel.
39324      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39325      */
39326     unhidePanel : function(panel){
39327         if(this.tabs && (panel = this.getPanel(panel))){
39328             this.tabs.unhideTab(panel.getEl().id);
39329         }
39330     },
39331
39332     clearPanels : function(){
39333         while(this.panels.getCount() > 0){
39334              this.remove(this.panels.first());
39335         }
39336     },
39337
39338     /**
39339      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39340      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39341      * @param {Boolean} preservePanel Overrides the config preservePanel option
39342      * @return {Roo.ContentPanel} The panel that was removed
39343      */
39344     remove : function(panel, preservePanel)
39345     {
39346         panel = this.getPanel(panel);
39347         if(!panel){
39348             return null;
39349         }
39350         var e = {};
39351         this.fireEvent("beforeremove", this, panel, e);
39352         if(e.cancel === true){
39353             return null;
39354         }
39355         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39356         var panelId = panel.getId();
39357         this.panels.removeKey(panelId);
39358         if(preservePanel){
39359             document.body.appendChild(panel.getEl().dom);
39360         }
39361         if(this.tabs){
39362             this.tabs.removeTab(panel.getEl().id);
39363         }else if (!preservePanel){
39364             this.bodyEl.dom.removeChild(panel.getEl().dom);
39365         }
39366         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39367             var p = this.panels.first();
39368             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39369             tempEl.appendChild(p.getEl().dom);
39370             this.bodyEl.update("");
39371             this.bodyEl.dom.appendChild(p.getEl().dom);
39372             tempEl = null;
39373             this.updateTitle(p.getTitle());
39374             this.tabs = null;
39375             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39376             this.setActivePanel(p);
39377         }
39378         panel.setRegion(null);
39379         if(this.activePanel == panel){
39380             this.activePanel = null;
39381         }
39382         if(this.config.autoDestroy !== false && preservePanel !== true){
39383             try{panel.destroy();}catch(e){}
39384         }
39385         this.fireEvent("panelremoved", this, panel);
39386         return panel;
39387     },
39388
39389     /**
39390      * Returns the TabPanel component used by this region
39391      * @return {Roo.TabPanel}
39392      */
39393     getTabs : function(){
39394         return this.tabs;
39395     },
39396
39397     createTool : function(parentEl, className){
39398         var btn = Roo.DomHelper.append(parentEl, {
39399             tag: "div",
39400             cls: "x-layout-tools-button",
39401             children: [ {
39402                 tag: "div",
39403                 cls: "roo-layout-tools-button-inner " + className,
39404                 html: "&#160;"
39405             }]
39406         }, true);
39407         btn.addClassOnOver("roo-layout-tools-button-over");
39408         return btn;
39409     }
39410 });/*
39411  * Based on:
39412  * Ext JS Library 1.1.1
39413  * Copyright(c) 2006-2007, Ext JS, LLC.
39414  *
39415  * Originally Released Under LGPL - original licence link has changed is not relivant.
39416  *
39417  * Fork - LGPL
39418  * <script type="text/javascript">
39419  */
39420  
39421
39422
39423 /**
39424  * @class Roo.SplitLayoutRegion
39425  * @extends Roo.LayoutRegion
39426  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39427  */
39428 Roo.bootstrap.layout.Split = function(config){
39429     this.cursor = config.cursor;
39430     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39431 };
39432
39433 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39434 {
39435     splitTip : "Drag to resize.",
39436     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39437     useSplitTips : false,
39438
39439     applyConfig : function(config){
39440         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39441     },
39442     
39443     onRender : function(ctr,pos) {
39444         
39445         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39446         if(!this.config.split){
39447             return;
39448         }
39449         if(!this.split){
39450             
39451             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39452                             tag: "div",
39453                             id: this.el.id + "-split",
39454                             cls: "roo-layout-split roo-layout-split-"+this.position,
39455                             html: "&#160;"
39456             });
39457             /** The SplitBar for this region 
39458             * @type Roo.SplitBar */
39459             // does not exist yet...
39460             Roo.log([this.position, this.orientation]);
39461             
39462             this.split = new Roo.bootstrap.SplitBar({
39463                 dragElement : splitEl,
39464                 resizingElement: this.el,
39465                 orientation : this.orientation
39466             });
39467             
39468             this.split.on("moved", this.onSplitMove, this);
39469             this.split.useShim = this.config.useShim === true;
39470             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39471             if(this.useSplitTips){
39472                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39473             }
39474             //if(config.collapsible){
39475             //    this.split.el.on("dblclick", this.collapse,  this);
39476             //}
39477         }
39478         if(typeof this.config.minSize != "undefined"){
39479             this.split.minSize = this.config.minSize;
39480         }
39481         if(typeof this.config.maxSize != "undefined"){
39482             this.split.maxSize = this.config.maxSize;
39483         }
39484         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39485             this.hideSplitter();
39486         }
39487         
39488     },
39489
39490     getHMaxSize : function(){
39491          var cmax = this.config.maxSize || 10000;
39492          var center = this.mgr.getRegion("center");
39493          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39494     },
39495
39496     getVMaxSize : function(){
39497          var cmax = this.config.maxSize || 10000;
39498          var center = this.mgr.getRegion("center");
39499          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39500     },
39501
39502     onSplitMove : function(split, newSize){
39503         this.fireEvent("resized", this, newSize);
39504     },
39505     
39506     /** 
39507      * Returns the {@link Roo.SplitBar} for this region.
39508      * @return {Roo.SplitBar}
39509      */
39510     getSplitBar : function(){
39511         return this.split;
39512     },
39513     
39514     hide : function(){
39515         this.hideSplitter();
39516         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39517     },
39518
39519     hideSplitter : function(){
39520         if(this.split){
39521             this.split.el.setLocation(-2000,-2000);
39522             this.split.el.hide();
39523         }
39524     },
39525
39526     show : function(){
39527         if(this.split){
39528             this.split.el.show();
39529         }
39530         Roo.bootstrap.layout.Split.superclass.show.call(this);
39531     },
39532     
39533     beforeSlide: function(){
39534         if(Roo.isGecko){// firefox overflow auto bug workaround
39535             this.bodyEl.clip();
39536             if(this.tabs) {
39537                 this.tabs.bodyEl.clip();
39538             }
39539             if(this.activePanel){
39540                 this.activePanel.getEl().clip();
39541                 
39542                 if(this.activePanel.beforeSlide){
39543                     this.activePanel.beforeSlide();
39544                 }
39545             }
39546         }
39547     },
39548     
39549     afterSlide : function(){
39550         if(Roo.isGecko){// firefox overflow auto bug workaround
39551             this.bodyEl.unclip();
39552             if(this.tabs) {
39553                 this.tabs.bodyEl.unclip();
39554             }
39555             if(this.activePanel){
39556                 this.activePanel.getEl().unclip();
39557                 if(this.activePanel.afterSlide){
39558                     this.activePanel.afterSlide();
39559                 }
39560             }
39561         }
39562     },
39563
39564     initAutoHide : function(){
39565         if(this.autoHide !== false){
39566             if(!this.autoHideHd){
39567                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39568                 this.autoHideHd = {
39569                     "mouseout": function(e){
39570                         if(!e.within(this.el, true)){
39571                             st.delay(500);
39572                         }
39573                     },
39574                     "mouseover" : function(e){
39575                         st.cancel();
39576                     },
39577                     scope : this
39578                 };
39579             }
39580             this.el.on(this.autoHideHd);
39581         }
39582     },
39583
39584     clearAutoHide : function(){
39585         if(this.autoHide !== false){
39586             this.el.un("mouseout", this.autoHideHd.mouseout);
39587             this.el.un("mouseover", this.autoHideHd.mouseover);
39588         }
39589     },
39590
39591     clearMonitor : function(){
39592         Roo.get(document).un("click", this.slideInIf, this);
39593     },
39594
39595     // these names are backwards but not changed for compat
39596     slideOut : function(){
39597         if(this.isSlid || this.el.hasActiveFx()){
39598             return;
39599         }
39600         this.isSlid = true;
39601         if(this.collapseBtn){
39602             this.collapseBtn.hide();
39603         }
39604         this.closeBtnState = this.closeBtn.getStyle('display');
39605         this.closeBtn.hide();
39606         if(this.stickBtn){
39607             this.stickBtn.show();
39608         }
39609         this.el.show();
39610         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39611         this.beforeSlide();
39612         this.el.setStyle("z-index", 10001);
39613         this.el.slideIn(this.getSlideAnchor(), {
39614             callback: function(){
39615                 this.afterSlide();
39616                 this.initAutoHide();
39617                 Roo.get(document).on("click", this.slideInIf, this);
39618                 this.fireEvent("slideshow", this);
39619             },
39620             scope: this,
39621             block: true
39622         });
39623     },
39624
39625     afterSlideIn : function(){
39626         this.clearAutoHide();
39627         this.isSlid = false;
39628         this.clearMonitor();
39629         this.el.setStyle("z-index", "");
39630         if(this.collapseBtn){
39631             this.collapseBtn.show();
39632         }
39633         this.closeBtn.setStyle('display', this.closeBtnState);
39634         if(this.stickBtn){
39635             this.stickBtn.hide();
39636         }
39637         this.fireEvent("slidehide", this);
39638     },
39639
39640     slideIn : function(cb){
39641         if(!this.isSlid || this.el.hasActiveFx()){
39642             Roo.callback(cb);
39643             return;
39644         }
39645         this.isSlid = false;
39646         this.beforeSlide();
39647         this.el.slideOut(this.getSlideAnchor(), {
39648             callback: function(){
39649                 this.el.setLeftTop(-10000, -10000);
39650                 this.afterSlide();
39651                 this.afterSlideIn();
39652                 Roo.callback(cb);
39653             },
39654             scope: this,
39655             block: true
39656         });
39657     },
39658     
39659     slideInIf : function(e){
39660         if(!e.within(this.el)){
39661             this.slideIn();
39662         }
39663     },
39664
39665     animateCollapse : function(){
39666         this.beforeSlide();
39667         this.el.setStyle("z-index", 20000);
39668         var anchor = this.getSlideAnchor();
39669         this.el.slideOut(anchor, {
39670             callback : function(){
39671                 this.el.setStyle("z-index", "");
39672                 this.collapsedEl.slideIn(anchor, {duration:.3});
39673                 this.afterSlide();
39674                 this.el.setLocation(-10000,-10000);
39675                 this.el.hide();
39676                 this.fireEvent("collapsed", this);
39677             },
39678             scope: this,
39679             block: true
39680         });
39681     },
39682
39683     animateExpand : function(){
39684         this.beforeSlide();
39685         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39686         this.el.setStyle("z-index", 20000);
39687         this.collapsedEl.hide({
39688             duration:.1
39689         });
39690         this.el.slideIn(this.getSlideAnchor(), {
39691             callback : function(){
39692                 this.el.setStyle("z-index", "");
39693                 this.afterSlide();
39694                 if(this.split){
39695                     this.split.el.show();
39696                 }
39697                 this.fireEvent("invalidated", this);
39698                 this.fireEvent("expanded", this);
39699             },
39700             scope: this,
39701             block: true
39702         });
39703     },
39704
39705     anchors : {
39706         "west" : "left",
39707         "east" : "right",
39708         "north" : "top",
39709         "south" : "bottom"
39710     },
39711
39712     sanchors : {
39713         "west" : "l",
39714         "east" : "r",
39715         "north" : "t",
39716         "south" : "b"
39717     },
39718
39719     canchors : {
39720         "west" : "tl-tr",
39721         "east" : "tr-tl",
39722         "north" : "tl-bl",
39723         "south" : "bl-tl"
39724     },
39725
39726     getAnchor : function(){
39727         return this.anchors[this.position];
39728     },
39729
39730     getCollapseAnchor : function(){
39731         return this.canchors[this.position];
39732     },
39733
39734     getSlideAnchor : function(){
39735         return this.sanchors[this.position];
39736     },
39737
39738     getAlignAdj : function(){
39739         var cm = this.cmargins;
39740         switch(this.position){
39741             case "west":
39742                 return [0, 0];
39743             break;
39744             case "east":
39745                 return [0, 0];
39746             break;
39747             case "north":
39748                 return [0, 0];
39749             break;
39750             case "south":
39751                 return [0, 0];
39752             break;
39753         }
39754     },
39755
39756     getExpandAdj : function(){
39757         var c = this.collapsedEl, cm = this.cmargins;
39758         switch(this.position){
39759             case "west":
39760                 return [-(cm.right+c.getWidth()+cm.left), 0];
39761             break;
39762             case "east":
39763                 return [cm.right+c.getWidth()+cm.left, 0];
39764             break;
39765             case "north":
39766                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39767             break;
39768             case "south":
39769                 return [0, cm.top+cm.bottom+c.getHeight()];
39770             break;
39771         }
39772     }
39773 });/*
39774  * Based on:
39775  * Ext JS Library 1.1.1
39776  * Copyright(c) 2006-2007, Ext JS, LLC.
39777  *
39778  * Originally Released Under LGPL - original licence link has changed is not relivant.
39779  *
39780  * Fork - LGPL
39781  * <script type="text/javascript">
39782  */
39783 /*
39784  * These classes are private internal classes
39785  */
39786 Roo.bootstrap.layout.Center = function(config){
39787     config.region = "center";
39788     Roo.bootstrap.layout.Region.call(this, config);
39789     this.visible = true;
39790     this.minWidth = config.minWidth || 20;
39791     this.minHeight = config.minHeight || 20;
39792 };
39793
39794 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39795     hide : function(){
39796         // center panel can't be hidden
39797     },
39798     
39799     show : function(){
39800         // center panel can't be hidden
39801     },
39802     
39803     getMinWidth: function(){
39804         return this.minWidth;
39805     },
39806     
39807     getMinHeight: function(){
39808         return this.minHeight;
39809     }
39810 });
39811
39812
39813
39814
39815  
39816
39817
39818
39819
39820
39821
39822 Roo.bootstrap.layout.North = function(config)
39823 {
39824     config.region = 'north';
39825     config.cursor = 'n-resize';
39826     
39827     Roo.bootstrap.layout.Split.call(this, config);
39828     
39829     
39830     if(this.split){
39831         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39832         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39833         this.split.el.addClass("roo-layout-split-v");
39834     }
39835     //var size = config.initialSize || config.height;
39836     //if(this.el && typeof size != "undefined"){
39837     //    this.el.setHeight(size);
39838     //}
39839 };
39840 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39841 {
39842     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39843      
39844      
39845     onRender : function(ctr, pos)
39846     {
39847         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39848         var size = this.config.initialSize || this.config.height;
39849         if(this.el && typeof size != "undefined"){
39850             this.el.setHeight(size);
39851         }
39852     
39853     },
39854     
39855     getBox : function(){
39856         if(this.collapsed){
39857             return this.collapsedEl.getBox();
39858         }
39859         var box = this.el.getBox();
39860         if(this.split){
39861             box.height += this.split.el.getHeight();
39862         }
39863         return box;
39864     },
39865     
39866     updateBox : function(box){
39867         if(this.split && !this.collapsed){
39868             box.height -= this.split.el.getHeight();
39869             this.split.el.setLeft(box.x);
39870             this.split.el.setTop(box.y+box.height);
39871             this.split.el.setWidth(box.width);
39872         }
39873         if(this.collapsed){
39874             this.updateBody(box.width, null);
39875         }
39876         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39877     }
39878 });
39879
39880
39881
39882
39883
39884 Roo.bootstrap.layout.South = function(config){
39885     config.region = 'south';
39886     config.cursor = 's-resize';
39887     Roo.bootstrap.layout.Split.call(this, config);
39888     if(this.split){
39889         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39890         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39891         this.split.el.addClass("roo-layout-split-v");
39892     }
39893     
39894 };
39895
39896 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39897     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39898     
39899     onRender : function(ctr, pos)
39900     {
39901         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39902         var size = this.config.initialSize || this.config.height;
39903         if(this.el && typeof size != "undefined"){
39904             this.el.setHeight(size);
39905         }
39906     
39907     },
39908     
39909     getBox : function(){
39910         if(this.collapsed){
39911             return this.collapsedEl.getBox();
39912         }
39913         var box = this.el.getBox();
39914         if(this.split){
39915             var sh = this.split.el.getHeight();
39916             box.height += sh;
39917             box.y -= sh;
39918         }
39919         return box;
39920     },
39921     
39922     updateBox : function(box){
39923         if(this.split && !this.collapsed){
39924             var sh = this.split.el.getHeight();
39925             box.height -= sh;
39926             box.y += sh;
39927             this.split.el.setLeft(box.x);
39928             this.split.el.setTop(box.y-sh);
39929             this.split.el.setWidth(box.width);
39930         }
39931         if(this.collapsed){
39932             this.updateBody(box.width, null);
39933         }
39934         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39935     }
39936 });
39937
39938 Roo.bootstrap.layout.East = function(config){
39939     config.region = "east";
39940     config.cursor = "e-resize";
39941     Roo.bootstrap.layout.Split.call(this, config);
39942     if(this.split){
39943         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39944         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39945         this.split.el.addClass("roo-layout-split-h");
39946     }
39947     
39948 };
39949 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39950     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39951     
39952     onRender : function(ctr, pos)
39953     {
39954         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39955         var size = this.config.initialSize || this.config.width;
39956         if(this.el && typeof size != "undefined"){
39957             this.el.setWidth(size);
39958         }
39959     
39960     },
39961     
39962     getBox : function(){
39963         if(this.collapsed){
39964             return this.collapsedEl.getBox();
39965         }
39966         var box = this.el.getBox();
39967         if(this.split){
39968             var sw = this.split.el.getWidth();
39969             box.width += sw;
39970             box.x -= sw;
39971         }
39972         return box;
39973     },
39974
39975     updateBox : function(box){
39976         if(this.split && !this.collapsed){
39977             var sw = this.split.el.getWidth();
39978             box.width -= sw;
39979             this.split.el.setLeft(box.x);
39980             this.split.el.setTop(box.y);
39981             this.split.el.setHeight(box.height);
39982             box.x += sw;
39983         }
39984         if(this.collapsed){
39985             this.updateBody(null, box.height);
39986         }
39987         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39988     }
39989 });
39990
39991 Roo.bootstrap.layout.West = function(config){
39992     config.region = "west";
39993     config.cursor = "w-resize";
39994     
39995     Roo.bootstrap.layout.Split.call(this, config);
39996     if(this.split){
39997         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39998         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39999         this.split.el.addClass("roo-layout-split-h");
40000     }
40001     
40002 };
40003 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40004     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40005     
40006     onRender: function(ctr, pos)
40007     {
40008         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40009         var size = this.config.initialSize || this.config.width;
40010         if(typeof size != "undefined"){
40011             this.el.setWidth(size);
40012         }
40013     },
40014     
40015     getBox : function(){
40016         if(this.collapsed){
40017             return this.collapsedEl.getBox();
40018         }
40019         var box = this.el.getBox();
40020         if (box.width == 0) {
40021             box.width = this.config.width; // kludge?
40022         }
40023         if(this.split){
40024             box.width += this.split.el.getWidth();
40025         }
40026         return box;
40027     },
40028     
40029     updateBox : function(box){
40030         if(this.split && !this.collapsed){
40031             var sw = this.split.el.getWidth();
40032             box.width -= sw;
40033             this.split.el.setLeft(box.x+box.width);
40034             this.split.el.setTop(box.y);
40035             this.split.el.setHeight(box.height);
40036         }
40037         if(this.collapsed){
40038             this.updateBody(null, box.height);
40039         }
40040         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40041     }
40042 });Roo.namespace("Roo.bootstrap.panel");/*
40043  * Based on:
40044  * Ext JS Library 1.1.1
40045  * Copyright(c) 2006-2007, Ext JS, LLC.
40046  *
40047  * Originally Released Under LGPL - original licence link has changed is not relivant.
40048  *
40049  * Fork - LGPL
40050  * <script type="text/javascript">
40051  */
40052 /**
40053  * @class Roo.ContentPanel
40054  * @extends Roo.util.Observable
40055  * A basic ContentPanel element.
40056  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40057  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40058  * @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
40059  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40060  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40061  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40062  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40063  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40064  * @cfg {String} title          The title for this panel
40065  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40066  * @cfg {String} url            Calls {@link #setUrl} with this value
40067  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40068  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40069  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40070  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40071  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40072  * @cfg {Boolean} badges render the badges
40073  * @cfg {String} cls  extra classes to use  
40074  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40075
40076  * @constructor
40077  * Create a new ContentPanel.
40078  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40079  * @param {String/Object} config A string to set only the title or a config object
40080  * @param {String} content (optional) Set the HTML content for this panel
40081  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40082  */
40083 Roo.bootstrap.panel.Content = function( config){
40084     
40085     this.tpl = config.tpl || false;
40086     
40087     var el = config.el;
40088     var content = config.content;
40089
40090     if(config.autoCreate){ // xtype is available if this is called from factory
40091         el = Roo.id();
40092     }
40093     this.el = Roo.get(el);
40094     if(!this.el && config && config.autoCreate){
40095         if(typeof config.autoCreate == "object"){
40096             if(!config.autoCreate.id){
40097                 config.autoCreate.id = config.id||el;
40098             }
40099             this.el = Roo.DomHelper.append(document.body,
40100                         config.autoCreate, true);
40101         }else{
40102             var elcfg =  {
40103                 tag: "div",
40104                 cls: (config.cls || '') +
40105                     (config.background ? ' bg-' + config.background : '') +
40106                     " roo-layout-inactive-content",
40107                 id: config.id||el
40108             };
40109             if (config.iframe) {
40110                 elcfg.cn = [
40111                     {
40112                         tag : 'iframe',
40113                         style : 'border: 0px',
40114                         src : 'about:blank'
40115                     }
40116                 ];
40117             }
40118               
40119             if (config.html) {
40120                 elcfg.html = config.html;
40121                 
40122             }
40123                         
40124             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40125             if (config.iframe) {
40126                 this.iframeEl = this.el.select('iframe',true).first();
40127             }
40128             
40129         }
40130     } 
40131     this.closable = false;
40132     this.loaded = false;
40133     this.active = false;
40134    
40135       
40136     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40137         
40138         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40139         
40140         this.wrapEl = this.el; //this.el.wrap();
40141         var ti = [];
40142         if (config.toolbar.items) {
40143             ti = config.toolbar.items ;
40144             delete config.toolbar.items ;
40145         }
40146         
40147         var nitems = [];
40148         this.toolbar.render(this.wrapEl, 'before');
40149         for(var i =0;i < ti.length;i++) {
40150           //  Roo.log(['add child', items[i]]);
40151             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40152         }
40153         this.toolbar.items = nitems;
40154         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40155         delete config.toolbar;
40156         
40157     }
40158     /*
40159     // xtype created footer. - not sure if will work as we normally have to render first..
40160     if (this.footer && !this.footer.el && this.footer.xtype) {
40161         if (!this.wrapEl) {
40162             this.wrapEl = this.el.wrap();
40163         }
40164     
40165         this.footer.container = this.wrapEl.createChild();
40166          
40167         this.footer = Roo.factory(this.footer, Roo);
40168         
40169     }
40170     */
40171     
40172      if(typeof config == "string"){
40173         this.title = config;
40174     }else{
40175         Roo.apply(this, config);
40176     }
40177     
40178     if(this.resizeEl){
40179         this.resizeEl = Roo.get(this.resizeEl, true);
40180     }else{
40181         this.resizeEl = this.el;
40182     }
40183     // handle view.xtype
40184     
40185  
40186     
40187     
40188     this.addEvents({
40189         /**
40190          * @event activate
40191          * Fires when this panel is activated. 
40192          * @param {Roo.ContentPanel} this
40193          */
40194         "activate" : true,
40195         /**
40196          * @event deactivate
40197          * Fires when this panel is activated. 
40198          * @param {Roo.ContentPanel} this
40199          */
40200         "deactivate" : true,
40201
40202         /**
40203          * @event resize
40204          * Fires when this panel is resized if fitToFrame is true.
40205          * @param {Roo.ContentPanel} this
40206          * @param {Number} width The width after any component adjustments
40207          * @param {Number} height The height after any component adjustments
40208          */
40209         "resize" : true,
40210         
40211          /**
40212          * @event render
40213          * Fires when this tab is created
40214          * @param {Roo.ContentPanel} this
40215          */
40216         "render" : true,
40217         
40218           /**
40219          * @event scroll
40220          * Fires when this content is scrolled
40221          * @param {Roo.ContentPanel} this
40222          * @param {Event} scrollEvent
40223          */
40224         "scroll" : true
40225         
40226         
40227         
40228     });
40229     
40230
40231     
40232     
40233     if(this.autoScroll && !this.iframe){
40234         this.resizeEl.setStyle("overflow", "auto");
40235         this.resizeEl.on('scroll', this.onScroll, this);
40236     } else {
40237         // fix randome scrolling
40238         //this.el.on('scroll', function() {
40239         //    Roo.log('fix random scolling');
40240         //    this.scrollTo('top',0); 
40241         //});
40242     }
40243     content = content || this.content;
40244     if(content){
40245         this.setContent(content);
40246     }
40247     if(config && config.url){
40248         this.setUrl(this.url, this.params, this.loadOnce);
40249     }
40250     
40251     
40252     
40253     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40254     
40255     if (this.view && typeof(this.view.xtype) != 'undefined') {
40256         this.view.el = this.el.appendChild(document.createElement("div"));
40257         this.view = Roo.factory(this.view); 
40258         this.view.render  &&  this.view.render(false, '');  
40259     }
40260     
40261     
40262     this.fireEvent('render', this);
40263 };
40264
40265 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40266     
40267     cls : '',
40268     background : '',
40269     
40270     tabTip : '',
40271     
40272     iframe : false,
40273     iframeEl : false,
40274     
40275     /* Resize Element - use this to work out scroll etc. */
40276     resizeEl : false,
40277     
40278     setRegion : function(region){
40279         this.region = region;
40280         this.setActiveClass(region && !this.background);
40281     },
40282     
40283     
40284     setActiveClass: function(state)
40285     {
40286         if(state){
40287            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40288            this.el.setStyle('position','relative');
40289         }else{
40290            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40291            this.el.setStyle('position', 'absolute');
40292         } 
40293     },
40294     
40295     /**
40296      * Returns the toolbar for this Panel if one was configured. 
40297      * @return {Roo.Toolbar} 
40298      */
40299     getToolbar : function(){
40300         return this.toolbar;
40301     },
40302     
40303     setActiveState : function(active)
40304     {
40305         this.active = active;
40306         this.setActiveClass(active);
40307         if(!active){
40308             if(this.fireEvent("deactivate", this) === false){
40309                 return false;
40310             }
40311             return true;
40312         }
40313         this.fireEvent("activate", this);
40314         return true;
40315     },
40316     /**
40317      * Updates this panel's element (not for iframe)
40318      * @param {String} content The new content
40319      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40320     */
40321     setContent : function(content, loadScripts){
40322         if (this.iframe) {
40323             return;
40324         }
40325         
40326         this.el.update(content, loadScripts);
40327     },
40328
40329     ignoreResize : function(w, h){
40330         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40331             return true;
40332         }else{
40333             this.lastSize = {width: w, height: h};
40334             return false;
40335         }
40336     },
40337     /**
40338      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40339      * @return {Roo.UpdateManager} The UpdateManager
40340      */
40341     getUpdateManager : function(){
40342         if (this.iframe) {
40343             return false;
40344         }
40345         return this.el.getUpdateManager();
40346     },
40347      /**
40348      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40349      * Does not work with IFRAME contents
40350      * @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:
40351 <pre><code>
40352 panel.load({
40353     url: "your-url.php",
40354     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40355     callback: yourFunction,
40356     scope: yourObject, //(optional scope)
40357     discardUrl: false,
40358     nocache: false,
40359     text: "Loading...",
40360     timeout: 30,
40361     scripts: false
40362 });
40363 </code></pre>
40364      
40365      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40366      * 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.
40367      * @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}
40368      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40369      * @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.
40370      * @return {Roo.ContentPanel} this
40371      */
40372     load : function(){
40373         
40374         if (this.iframe) {
40375             return this;
40376         }
40377         
40378         var um = this.el.getUpdateManager();
40379         um.update.apply(um, arguments);
40380         return this;
40381     },
40382
40383
40384     /**
40385      * 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.
40386      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40387      * @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)
40388      * @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)
40389      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40390      */
40391     setUrl : function(url, params, loadOnce){
40392         if (this.iframe) {
40393             this.iframeEl.dom.src = url;
40394             return false;
40395         }
40396         
40397         if(this.refreshDelegate){
40398             this.removeListener("activate", this.refreshDelegate);
40399         }
40400         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40401         this.on("activate", this.refreshDelegate);
40402         return this.el.getUpdateManager();
40403     },
40404     
40405     _handleRefresh : function(url, params, loadOnce){
40406         if(!loadOnce || !this.loaded){
40407             var updater = this.el.getUpdateManager();
40408             updater.update(url, params, this._setLoaded.createDelegate(this));
40409         }
40410     },
40411     
40412     _setLoaded : function(){
40413         this.loaded = true;
40414     }, 
40415     
40416     /**
40417      * Returns this panel's id
40418      * @return {String} 
40419      */
40420     getId : function(){
40421         return this.el.id;
40422     },
40423     
40424     /** 
40425      * Returns this panel's element - used by regiosn to add.
40426      * @return {Roo.Element} 
40427      */
40428     getEl : function(){
40429         return this.wrapEl || this.el;
40430     },
40431     
40432    
40433     
40434     adjustForComponents : function(width, height)
40435     {
40436         //Roo.log('adjustForComponents ');
40437         if(this.resizeEl != this.el){
40438             width -= this.el.getFrameWidth('lr');
40439             height -= this.el.getFrameWidth('tb');
40440         }
40441         if(this.toolbar){
40442             var te = this.toolbar.getEl();
40443             te.setWidth(width);
40444             height -= te.getHeight();
40445         }
40446         if(this.footer){
40447             var te = this.footer.getEl();
40448             te.setWidth(width);
40449             height -= te.getHeight();
40450         }
40451         
40452         
40453         if(this.adjustments){
40454             width += this.adjustments[0];
40455             height += this.adjustments[1];
40456         }
40457         return {"width": width, "height": height};
40458     },
40459     
40460     setSize : function(width, height){
40461         if(this.fitToFrame && !this.ignoreResize(width, height)){
40462             if(this.fitContainer && this.resizeEl != this.el){
40463                 this.el.setSize(width, height);
40464             }
40465             var size = this.adjustForComponents(width, height);
40466             if (this.iframe) {
40467                 this.iframeEl.setSize(width,height);
40468             }
40469             
40470             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40471             this.fireEvent('resize', this, size.width, size.height);
40472             
40473             
40474         }
40475     },
40476     
40477     /**
40478      * Returns this panel's title
40479      * @return {String} 
40480      */
40481     getTitle : function(){
40482         
40483         if (typeof(this.title) != 'object') {
40484             return this.title;
40485         }
40486         
40487         var t = '';
40488         for (var k in this.title) {
40489             if (!this.title.hasOwnProperty(k)) {
40490                 continue;
40491             }
40492             
40493             if (k.indexOf('-') >= 0) {
40494                 var s = k.split('-');
40495                 for (var i = 0; i<s.length; i++) {
40496                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40497                 }
40498             } else {
40499                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40500             }
40501         }
40502         return t;
40503     },
40504     
40505     /**
40506      * Set this panel's title
40507      * @param {String} title
40508      */
40509     setTitle : function(title){
40510         this.title = title;
40511         if(this.region){
40512             this.region.updatePanelTitle(this, title);
40513         }
40514     },
40515     
40516     /**
40517      * Returns true is this panel was configured to be closable
40518      * @return {Boolean} 
40519      */
40520     isClosable : function(){
40521         return this.closable;
40522     },
40523     
40524     beforeSlide : function(){
40525         this.el.clip();
40526         this.resizeEl.clip();
40527     },
40528     
40529     afterSlide : function(){
40530         this.el.unclip();
40531         this.resizeEl.unclip();
40532     },
40533     
40534     /**
40535      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40536      *   Will fail silently if the {@link #setUrl} method has not been called.
40537      *   This does not activate the panel, just updates its content.
40538      */
40539     refresh : function(){
40540         if(this.refreshDelegate){
40541            this.loaded = false;
40542            this.refreshDelegate();
40543         }
40544     },
40545     
40546     /**
40547      * Destroys this panel
40548      */
40549     destroy : function(){
40550         this.el.removeAllListeners();
40551         var tempEl = document.createElement("span");
40552         tempEl.appendChild(this.el.dom);
40553         tempEl.innerHTML = "";
40554         this.el.remove();
40555         this.el = null;
40556     },
40557     
40558     /**
40559      * form - if the content panel contains a form - this is a reference to it.
40560      * @type {Roo.form.Form}
40561      */
40562     form : false,
40563     /**
40564      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40565      *    This contains a reference to it.
40566      * @type {Roo.View}
40567      */
40568     view : false,
40569     
40570       /**
40571      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40572      * <pre><code>
40573
40574 layout.addxtype({
40575        xtype : 'Form',
40576        items: [ .... ]
40577    }
40578 );
40579
40580 </code></pre>
40581      * @param {Object} cfg Xtype definition of item to add.
40582      */
40583     
40584     
40585     getChildContainer: function () {
40586         return this.getEl();
40587     },
40588     
40589     
40590     onScroll : function(e)
40591     {
40592         this.fireEvent('scroll', this, e);
40593     }
40594     
40595     
40596     /*
40597         var  ret = new Roo.factory(cfg);
40598         return ret;
40599         
40600         
40601         // add form..
40602         if (cfg.xtype.match(/^Form$/)) {
40603             
40604             var el;
40605             //if (this.footer) {
40606             //    el = this.footer.container.insertSibling(false, 'before');
40607             //} else {
40608                 el = this.el.createChild();
40609             //}
40610
40611             this.form = new  Roo.form.Form(cfg);
40612             
40613             
40614             if ( this.form.allItems.length) {
40615                 this.form.render(el.dom);
40616             }
40617             return this.form;
40618         }
40619         // should only have one of theses..
40620         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40621             // views.. should not be just added - used named prop 'view''
40622             
40623             cfg.el = this.el.appendChild(document.createElement("div"));
40624             // factory?
40625             
40626             var ret = new Roo.factory(cfg);
40627              
40628              ret.render && ret.render(false, ''); // render blank..
40629             this.view = ret;
40630             return ret;
40631         }
40632         return false;
40633     }
40634     \*/
40635 });
40636  
40637 /**
40638  * @class Roo.bootstrap.panel.Grid
40639  * @extends Roo.bootstrap.panel.Content
40640  * @constructor
40641  * Create a new GridPanel.
40642  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40643  * @param {Object} config A the config object
40644   
40645  */
40646
40647
40648
40649 Roo.bootstrap.panel.Grid = function(config)
40650 {
40651     
40652       
40653     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40654         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40655
40656     config.el = this.wrapper;
40657     //this.el = this.wrapper;
40658     
40659       if (config.container) {
40660         // ctor'ed from a Border/panel.grid
40661         
40662         
40663         this.wrapper.setStyle("overflow", "hidden");
40664         this.wrapper.addClass('roo-grid-container');
40665
40666     }
40667     
40668     
40669     if(config.toolbar){
40670         var tool_el = this.wrapper.createChild();    
40671         this.toolbar = Roo.factory(config.toolbar);
40672         var ti = [];
40673         if (config.toolbar.items) {
40674             ti = config.toolbar.items ;
40675             delete config.toolbar.items ;
40676         }
40677         
40678         var nitems = [];
40679         this.toolbar.render(tool_el);
40680         for(var i =0;i < ti.length;i++) {
40681           //  Roo.log(['add child', items[i]]);
40682             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40683         }
40684         this.toolbar.items = nitems;
40685         
40686         delete config.toolbar;
40687     }
40688     
40689     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40690     config.grid.scrollBody = true;;
40691     config.grid.monitorWindowResize = false; // turn off autosizing
40692     config.grid.autoHeight = false;
40693     config.grid.autoWidth = false;
40694     
40695     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40696     
40697     if (config.background) {
40698         // render grid on panel activation (if panel background)
40699         this.on('activate', function(gp) {
40700             if (!gp.grid.rendered) {
40701                 gp.grid.render(this.wrapper);
40702                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40703             }
40704         });
40705             
40706     } else {
40707         this.grid.render(this.wrapper);
40708         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40709
40710     }
40711     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40712     // ??? needed ??? config.el = this.wrapper;
40713     
40714     
40715     
40716   
40717     // xtype created footer. - not sure if will work as we normally have to render first..
40718     if (this.footer && !this.footer.el && this.footer.xtype) {
40719         
40720         var ctr = this.grid.getView().getFooterPanel(true);
40721         this.footer.dataSource = this.grid.dataSource;
40722         this.footer = Roo.factory(this.footer, Roo);
40723         this.footer.render(ctr);
40724         
40725     }
40726     
40727     
40728     
40729     
40730      
40731 };
40732
40733 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40734     getId : function(){
40735         return this.grid.id;
40736     },
40737     
40738     /**
40739      * Returns the grid for this panel
40740      * @return {Roo.bootstrap.Table} 
40741      */
40742     getGrid : function(){
40743         return this.grid;    
40744     },
40745     
40746     setSize : function(width, height){
40747         if(!this.ignoreResize(width, height)){
40748             var grid = this.grid;
40749             var size = this.adjustForComponents(width, height);
40750             // tfoot is not a footer?
40751           
40752             
40753             var gridel = grid.getGridEl();
40754             gridel.setSize(size.width, size.height);
40755             
40756             var tbd = grid.getGridEl().select('tbody', true).first();
40757             var thd = grid.getGridEl().select('thead',true).first();
40758             var tbf= grid.getGridEl().select('tfoot', true).first();
40759
40760             if (tbf) {
40761                 size.height -= tbf.getHeight();
40762             }
40763             if (thd) {
40764                 size.height -= thd.getHeight();
40765             }
40766             
40767             tbd.setSize(size.width, size.height );
40768             // this is for the account management tab -seems to work there.
40769             var thd = grid.getGridEl().select('thead',true).first();
40770             //if (tbd) {
40771             //    tbd.setSize(size.width, size.height - thd.getHeight());
40772             //}
40773              
40774             grid.autoSize();
40775         }
40776     },
40777      
40778     
40779     
40780     beforeSlide : function(){
40781         this.grid.getView().scroller.clip();
40782     },
40783     
40784     afterSlide : function(){
40785         this.grid.getView().scroller.unclip();
40786     },
40787     
40788     destroy : function(){
40789         this.grid.destroy();
40790         delete this.grid;
40791         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40792     }
40793 });
40794
40795 /**
40796  * @class Roo.bootstrap.panel.Nest
40797  * @extends Roo.bootstrap.panel.Content
40798  * @constructor
40799  * Create a new Panel, that can contain a layout.Border.
40800  * 
40801  * 
40802  * @param {Roo.BorderLayout} layout The layout for this panel
40803  * @param {String/Object} config A string to set only the title or a config object
40804  */
40805 Roo.bootstrap.panel.Nest = function(config)
40806 {
40807     // construct with only one argument..
40808     /* FIXME - implement nicer consturctors
40809     if (layout.layout) {
40810         config = layout;
40811         layout = config.layout;
40812         delete config.layout;
40813     }
40814     if (layout.xtype && !layout.getEl) {
40815         // then layout needs constructing..
40816         layout = Roo.factory(layout, Roo);
40817     }
40818     */
40819     
40820     config.el =  config.layout.getEl();
40821     
40822     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40823     
40824     config.layout.monitorWindowResize = false; // turn off autosizing
40825     this.layout = config.layout;
40826     this.layout.getEl().addClass("roo-layout-nested-layout");
40827     this.layout.parent = this;
40828     
40829     
40830     
40831     
40832 };
40833
40834 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40835
40836     setSize : function(width, height){
40837         if(!this.ignoreResize(width, height)){
40838             var size = this.adjustForComponents(width, height);
40839             var el = this.layout.getEl();
40840             if (size.height < 1) {
40841                 el.setWidth(size.width);   
40842             } else {
40843                 el.setSize(size.width, size.height);
40844             }
40845             var touch = el.dom.offsetWidth;
40846             this.layout.layout();
40847             // ie requires a double layout on the first pass
40848             if(Roo.isIE && !this.initialized){
40849                 this.initialized = true;
40850                 this.layout.layout();
40851             }
40852         }
40853     },
40854     
40855     // activate all subpanels if not currently active..
40856     
40857     setActiveState : function(active){
40858         this.active = active;
40859         this.setActiveClass(active);
40860         
40861         if(!active){
40862             this.fireEvent("deactivate", this);
40863             return;
40864         }
40865         
40866         this.fireEvent("activate", this);
40867         // not sure if this should happen before or after..
40868         if (!this.layout) {
40869             return; // should not happen..
40870         }
40871         var reg = false;
40872         for (var r in this.layout.regions) {
40873             reg = this.layout.getRegion(r);
40874             if (reg.getActivePanel()) {
40875                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40876                 reg.setActivePanel(reg.getActivePanel());
40877                 continue;
40878             }
40879             if (!reg.panels.length) {
40880                 continue;
40881             }
40882             reg.showPanel(reg.getPanel(0));
40883         }
40884         
40885         
40886         
40887         
40888     },
40889     
40890     /**
40891      * Returns the nested BorderLayout for this panel
40892      * @return {Roo.BorderLayout} 
40893      */
40894     getLayout : function(){
40895         return this.layout;
40896     },
40897     
40898      /**
40899      * Adds a xtype elements to the layout of the nested panel
40900      * <pre><code>
40901
40902 panel.addxtype({
40903        xtype : 'ContentPanel',
40904        region: 'west',
40905        items: [ .... ]
40906    }
40907 );
40908
40909 panel.addxtype({
40910         xtype : 'NestedLayoutPanel',
40911         region: 'west',
40912         layout: {
40913            center: { },
40914            west: { }   
40915         },
40916         items : [ ... list of content panels or nested layout panels.. ]
40917    }
40918 );
40919 </code></pre>
40920      * @param {Object} cfg Xtype definition of item to add.
40921      */
40922     addxtype : function(cfg) {
40923         return this.layout.addxtype(cfg);
40924     
40925     }
40926 });/*
40927  * Based on:
40928  * Ext JS Library 1.1.1
40929  * Copyright(c) 2006-2007, Ext JS, LLC.
40930  *
40931  * Originally Released Under LGPL - original licence link has changed is not relivant.
40932  *
40933  * Fork - LGPL
40934  * <script type="text/javascript">
40935  */
40936 /**
40937  * @class Roo.TabPanel
40938  * @extends Roo.util.Observable
40939  * A lightweight tab container.
40940  * <br><br>
40941  * Usage:
40942  * <pre><code>
40943 // basic tabs 1, built from existing content
40944 var tabs = new Roo.TabPanel("tabs1");
40945 tabs.addTab("script", "View Script");
40946 tabs.addTab("markup", "View Markup");
40947 tabs.activate("script");
40948
40949 // more advanced tabs, built from javascript
40950 var jtabs = new Roo.TabPanel("jtabs");
40951 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40952
40953 // set up the UpdateManager
40954 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40955 var updater = tab2.getUpdateManager();
40956 updater.setDefaultUrl("ajax1.htm");
40957 tab2.on('activate', updater.refresh, updater, true);
40958
40959 // Use setUrl for Ajax loading
40960 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40961 tab3.setUrl("ajax2.htm", null, true);
40962
40963 // Disabled tab
40964 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40965 tab4.disable();
40966
40967 jtabs.activate("jtabs-1");
40968  * </code></pre>
40969  * @constructor
40970  * Create a new TabPanel.
40971  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40972  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40973  */
40974 Roo.bootstrap.panel.Tabs = function(config){
40975     /**
40976     * The container element for this TabPanel.
40977     * @type Roo.Element
40978     */
40979     this.el = Roo.get(config.el);
40980     delete config.el;
40981     if(config){
40982         if(typeof config == "boolean"){
40983             this.tabPosition = config ? "bottom" : "top";
40984         }else{
40985             Roo.apply(this, config);
40986         }
40987     }
40988     
40989     if(this.tabPosition == "bottom"){
40990         // if tabs are at the bottom = create the body first.
40991         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40992         this.el.addClass("roo-tabs-bottom");
40993     }
40994     // next create the tabs holders
40995     
40996     if (this.tabPosition == "west"){
40997         
40998         var reg = this.region; // fake it..
40999         while (reg) {
41000             if (!reg.mgr.parent) {
41001                 break;
41002             }
41003             reg = reg.mgr.parent.region;
41004         }
41005         Roo.log("got nest?");
41006         Roo.log(reg);
41007         if (reg.mgr.getRegion('west')) {
41008             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41009             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41010             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41011             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41012             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41013         
41014             
41015         }
41016         
41017         
41018     } else {
41019      
41020         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41021         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41022         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41023         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41024     }
41025     
41026     
41027     if(Roo.isIE){
41028         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41029     }
41030     
41031     // finally - if tabs are at the top, then create the body last..
41032     if(this.tabPosition != "bottom"){
41033         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41034          * @type Roo.Element
41035          */
41036         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41037         this.el.addClass("roo-tabs-top");
41038     }
41039     this.items = [];
41040
41041     this.bodyEl.setStyle("position", "relative");
41042
41043     this.active = null;
41044     this.activateDelegate = this.activate.createDelegate(this);
41045
41046     this.addEvents({
41047         /**
41048          * @event tabchange
41049          * Fires when the active tab changes
41050          * @param {Roo.TabPanel} this
41051          * @param {Roo.TabPanelItem} activePanel The new active tab
41052          */
41053         "tabchange": true,
41054         /**
41055          * @event beforetabchange
41056          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41057          * @param {Roo.TabPanel} this
41058          * @param {Object} e Set cancel to true on this object to cancel the tab change
41059          * @param {Roo.TabPanelItem} tab The tab being changed to
41060          */
41061         "beforetabchange" : true
41062     });
41063
41064     Roo.EventManager.onWindowResize(this.onResize, this);
41065     this.cpad = this.el.getPadding("lr");
41066     this.hiddenCount = 0;
41067
41068
41069     // toolbar on the tabbar support...
41070     if (this.toolbar) {
41071         alert("no toolbar support yet");
41072         this.toolbar  = false;
41073         /*
41074         var tcfg = this.toolbar;
41075         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41076         this.toolbar = new Roo.Toolbar(tcfg);
41077         if (Roo.isSafari) {
41078             var tbl = tcfg.container.child('table', true);
41079             tbl.setAttribute('width', '100%');
41080         }
41081         */
41082         
41083     }
41084    
41085
41086
41087     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41088 };
41089
41090 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41091     /*
41092      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41093      */
41094     tabPosition : "top",
41095     /*
41096      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41097      */
41098     currentTabWidth : 0,
41099     /*
41100      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41101      */
41102     minTabWidth : 40,
41103     /*
41104      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41105      */
41106     maxTabWidth : 250,
41107     /*
41108      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41109      */
41110     preferredTabWidth : 175,
41111     /*
41112      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41113      */
41114     resizeTabs : false,
41115     /*
41116      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41117      */
41118     monitorResize : true,
41119     /*
41120      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41121      */
41122     toolbar : false,  // set by caller..
41123     
41124     region : false, /// set by caller
41125     
41126     disableTooltips : true, // not used yet...
41127
41128     /**
41129      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41130      * @param {String} id The id of the div to use <b>or create</b>
41131      * @param {String} text The text for the tab
41132      * @param {String} content (optional) Content to put in the TabPanelItem body
41133      * @param {Boolean} closable (optional) True to create a close icon on the tab
41134      * @return {Roo.TabPanelItem} The created TabPanelItem
41135      */
41136     addTab : function(id, text, content, closable, tpl)
41137     {
41138         var item = new Roo.bootstrap.panel.TabItem({
41139             panel: this,
41140             id : id,
41141             text : text,
41142             closable : closable,
41143             tpl : tpl
41144         });
41145         this.addTabItem(item);
41146         if(content){
41147             item.setContent(content);
41148         }
41149         return item;
41150     },
41151
41152     /**
41153      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41154      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41155      * @return {Roo.TabPanelItem}
41156      */
41157     getTab : function(id){
41158         return this.items[id];
41159     },
41160
41161     /**
41162      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41163      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41164      */
41165     hideTab : function(id){
41166         var t = this.items[id];
41167         if(!t.isHidden()){
41168            t.setHidden(true);
41169            this.hiddenCount++;
41170            this.autoSizeTabs();
41171         }
41172     },
41173
41174     /**
41175      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41176      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41177      */
41178     unhideTab : function(id){
41179         var t = this.items[id];
41180         if(t.isHidden()){
41181            t.setHidden(false);
41182            this.hiddenCount--;
41183            this.autoSizeTabs();
41184         }
41185     },
41186
41187     /**
41188      * Adds an existing {@link Roo.TabPanelItem}.
41189      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41190      */
41191     addTabItem : function(item)
41192     {
41193         this.items[item.id] = item;
41194         this.items.push(item);
41195         this.autoSizeTabs();
41196       //  if(this.resizeTabs){
41197     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41198   //         this.autoSizeTabs();
41199 //        }else{
41200 //            item.autoSize();
41201        // }
41202     },
41203
41204     /**
41205      * Removes a {@link Roo.TabPanelItem}.
41206      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41207      */
41208     removeTab : function(id){
41209         var items = this.items;
41210         var tab = items[id];
41211         if(!tab) { return; }
41212         var index = items.indexOf(tab);
41213         if(this.active == tab && items.length > 1){
41214             var newTab = this.getNextAvailable(index);
41215             if(newTab) {
41216                 newTab.activate();
41217             }
41218         }
41219         this.stripEl.dom.removeChild(tab.pnode.dom);
41220         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41221             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41222         }
41223         items.splice(index, 1);
41224         delete this.items[tab.id];
41225         tab.fireEvent("close", tab);
41226         tab.purgeListeners();
41227         this.autoSizeTabs();
41228     },
41229
41230     getNextAvailable : function(start){
41231         var items = this.items;
41232         var index = start;
41233         // look for a next tab that will slide over to
41234         // replace the one being removed
41235         while(index < items.length){
41236             var item = items[++index];
41237             if(item && !item.isHidden()){
41238                 return item;
41239             }
41240         }
41241         // if one isn't found select the previous tab (on the left)
41242         index = start;
41243         while(index >= 0){
41244             var item = items[--index];
41245             if(item && !item.isHidden()){
41246                 return item;
41247             }
41248         }
41249         return null;
41250     },
41251
41252     /**
41253      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41254      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41255      */
41256     disableTab : function(id){
41257         var tab = this.items[id];
41258         if(tab && this.active != tab){
41259             tab.disable();
41260         }
41261     },
41262
41263     /**
41264      * Enables a {@link Roo.TabPanelItem} that is disabled.
41265      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41266      */
41267     enableTab : function(id){
41268         var tab = this.items[id];
41269         tab.enable();
41270     },
41271
41272     /**
41273      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41274      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41275      * @return {Roo.TabPanelItem} The TabPanelItem.
41276      */
41277     activate : function(id)
41278     {
41279         //Roo.log('activite:'  + id);
41280         
41281         var tab = this.items[id];
41282         if(!tab){
41283             return null;
41284         }
41285         if(tab == this.active || tab.disabled){
41286             return tab;
41287         }
41288         var e = {};
41289         this.fireEvent("beforetabchange", this, e, tab);
41290         if(e.cancel !== true && !tab.disabled){
41291             if(this.active){
41292                 this.active.hide();
41293             }
41294             this.active = this.items[id];
41295             this.active.show();
41296             this.fireEvent("tabchange", this, this.active);
41297         }
41298         return tab;
41299     },
41300
41301     /**
41302      * Gets the active {@link Roo.TabPanelItem}.
41303      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41304      */
41305     getActiveTab : function(){
41306         return this.active;
41307     },
41308
41309     /**
41310      * Updates the tab body element to fit the height of the container element
41311      * for overflow scrolling
41312      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41313      */
41314     syncHeight : function(targetHeight){
41315         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41316         var bm = this.bodyEl.getMargins();
41317         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41318         this.bodyEl.setHeight(newHeight);
41319         return newHeight;
41320     },
41321
41322     onResize : function(){
41323         if(this.monitorResize){
41324             this.autoSizeTabs();
41325         }
41326     },
41327
41328     /**
41329      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41330      */
41331     beginUpdate : function(){
41332         this.updating = true;
41333     },
41334
41335     /**
41336      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41337      */
41338     endUpdate : function(){
41339         this.updating = false;
41340         this.autoSizeTabs();
41341     },
41342
41343     /**
41344      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41345      */
41346     autoSizeTabs : function()
41347     {
41348         var count = this.items.length;
41349         var vcount = count - this.hiddenCount;
41350         
41351         if (vcount < 2) {
41352             this.stripEl.hide();
41353         } else {
41354             this.stripEl.show();
41355         }
41356         
41357         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41358             return;
41359         }
41360         
41361         
41362         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41363         var availWidth = Math.floor(w / vcount);
41364         var b = this.stripBody;
41365         if(b.getWidth() > w){
41366             var tabs = this.items;
41367             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41368             if(availWidth < this.minTabWidth){
41369                 /*if(!this.sleft){    // incomplete scrolling code
41370                     this.createScrollButtons();
41371                 }
41372                 this.showScroll();
41373                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41374             }
41375         }else{
41376             if(this.currentTabWidth < this.preferredTabWidth){
41377                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41378             }
41379         }
41380     },
41381
41382     /**
41383      * Returns the number of tabs in this TabPanel.
41384      * @return {Number}
41385      */
41386      getCount : function(){
41387          return this.items.length;
41388      },
41389
41390     /**
41391      * Resizes all the tabs to the passed width
41392      * @param {Number} The new width
41393      */
41394     setTabWidth : function(width){
41395         this.currentTabWidth = width;
41396         for(var i = 0, len = this.items.length; i < len; i++) {
41397                 if(!this.items[i].isHidden()) {
41398                 this.items[i].setWidth(width);
41399             }
41400         }
41401     },
41402
41403     /**
41404      * Destroys this TabPanel
41405      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41406      */
41407     destroy : function(removeEl){
41408         Roo.EventManager.removeResizeListener(this.onResize, this);
41409         for(var i = 0, len = this.items.length; i < len; i++){
41410             this.items[i].purgeListeners();
41411         }
41412         if(removeEl === true){
41413             this.el.update("");
41414             this.el.remove();
41415         }
41416     },
41417     
41418     createStrip : function(container)
41419     {
41420         var strip = document.createElement("nav");
41421         strip.className = Roo.bootstrap.version == 4 ?
41422             "navbar-light bg-light" : 
41423             "navbar navbar-default"; //"x-tabs-wrap";
41424         container.appendChild(strip);
41425         return strip;
41426     },
41427     
41428     createStripList : function(strip)
41429     {
41430         // div wrapper for retard IE
41431         // returns the "tr" element.
41432         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41433         //'<div class="x-tabs-strip-wrap">'+
41434           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41435           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41436         return strip.firstChild; //.firstChild.firstChild.firstChild;
41437     },
41438     createBody : function(container)
41439     {
41440         var body = document.createElement("div");
41441         Roo.id(body, "tab-body");
41442         //Roo.fly(body).addClass("x-tabs-body");
41443         Roo.fly(body).addClass("tab-content");
41444         container.appendChild(body);
41445         return body;
41446     },
41447     createItemBody :function(bodyEl, id){
41448         var body = Roo.getDom(id);
41449         if(!body){
41450             body = document.createElement("div");
41451             body.id = id;
41452         }
41453         //Roo.fly(body).addClass("x-tabs-item-body");
41454         Roo.fly(body).addClass("tab-pane");
41455          bodyEl.insertBefore(body, bodyEl.firstChild);
41456         return body;
41457     },
41458     /** @private */
41459     createStripElements :  function(stripEl, text, closable, tpl)
41460     {
41461         var td = document.createElement("li"); // was td..
41462         td.className = 'nav-item';
41463         
41464         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41465         
41466         
41467         stripEl.appendChild(td);
41468         /*if(closable){
41469             td.className = "x-tabs-closable";
41470             if(!this.closeTpl){
41471                 this.closeTpl = new Roo.Template(
41472                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41473                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41474                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41475                 );
41476             }
41477             var el = this.closeTpl.overwrite(td, {"text": text});
41478             var close = el.getElementsByTagName("div")[0];
41479             var inner = el.getElementsByTagName("em")[0];
41480             return {"el": el, "close": close, "inner": inner};
41481         } else {
41482         */
41483         // not sure what this is..
41484 //            if(!this.tabTpl){
41485                 //this.tabTpl = new Roo.Template(
41486                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41487                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41488                 //);
41489 //                this.tabTpl = new Roo.Template(
41490 //                   '<a href="#">' +
41491 //                   '<span unselectable="on"' +
41492 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41493 //                            ' >{text}</span></a>'
41494 //                );
41495 //                
41496 //            }
41497
41498
41499             var template = tpl || this.tabTpl || false;
41500             
41501             if(!template){
41502                 template =  new Roo.Template(
41503                         Roo.bootstrap.version == 4 ? 
41504                             (
41505                                 '<a class="nav-link" href="#" unselectable="on"' +
41506                                      (this.disableTooltips ? '' : ' title="{text}"') +
41507                                      ' >{text}</a>'
41508                             ) : (
41509                                 '<a class="nav-link" href="#">' +
41510                                 '<span unselectable="on"' +
41511                                          (this.disableTooltips ? '' : ' title="{text}"') +
41512                                     ' >{text}</span></a>'
41513                             )
41514                 );
41515             }
41516             
41517             switch (typeof(template)) {
41518                 case 'object' :
41519                     break;
41520                 case 'string' :
41521                     template = new Roo.Template(template);
41522                     break;
41523                 default :
41524                     break;
41525             }
41526             
41527             var el = template.overwrite(td, {"text": text});
41528             
41529             var inner = el.getElementsByTagName("span")[0];
41530             
41531             return {"el": el, "inner": inner};
41532             
41533     }
41534         
41535     
41536 });
41537
41538 /**
41539  * @class Roo.TabPanelItem
41540  * @extends Roo.util.Observable
41541  * Represents an individual item (tab plus body) in a TabPanel.
41542  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41543  * @param {String} id The id of this TabPanelItem
41544  * @param {String} text The text for the tab of this TabPanelItem
41545  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41546  */
41547 Roo.bootstrap.panel.TabItem = function(config){
41548     /**
41549      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41550      * @type Roo.TabPanel
41551      */
41552     this.tabPanel = config.panel;
41553     /**
41554      * The id for this TabPanelItem
41555      * @type String
41556      */
41557     this.id = config.id;
41558     /** @private */
41559     this.disabled = false;
41560     /** @private */
41561     this.text = config.text;
41562     /** @private */
41563     this.loaded = false;
41564     this.closable = config.closable;
41565
41566     /**
41567      * The body element for this TabPanelItem.
41568      * @type Roo.Element
41569      */
41570     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41571     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41572     this.bodyEl.setStyle("display", "block");
41573     this.bodyEl.setStyle("zoom", "1");
41574     //this.hideAction();
41575
41576     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41577     /** @private */
41578     this.el = Roo.get(els.el);
41579     this.inner = Roo.get(els.inner, true);
41580      this.textEl = Roo.bootstrap.version == 4 ?
41581         this.el : Roo.get(this.el.dom.firstChild, true);
41582
41583     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41584     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41585
41586     
41587 //    this.el.on("mousedown", this.onTabMouseDown, this);
41588     this.el.on("click", this.onTabClick, this);
41589     /** @private */
41590     if(config.closable){
41591         var c = Roo.get(els.close, true);
41592         c.dom.title = this.closeText;
41593         c.addClassOnOver("close-over");
41594         c.on("click", this.closeClick, this);
41595      }
41596
41597     this.addEvents({
41598          /**
41599          * @event activate
41600          * Fires when this tab becomes the active tab.
41601          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41602          * @param {Roo.TabPanelItem} this
41603          */
41604         "activate": true,
41605         /**
41606          * @event beforeclose
41607          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41608          * @param {Roo.TabPanelItem} this
41609          * @param {Object} e Set cancel to true on this object to cancel the close.
41610          */
41611         "beforeclose": true,
41612         /**
41613          * @event close
41614          * Fires when this tab is closed.
41615          * @param {Roo.TabPanelItem} this
41616          */
41617          "close": true,
41618         /**
41619          * @event deactivate
41620          * Fires when this tab is no longer the active tab.
41621          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41622          * @param {Roo.TabPanelItem} this
41623          */
41624          "deactivate" : true
41625     });
41626     this.hidden = false;
41627
41628     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41629 };
41630
41631 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41632            {
41633     purgeListeners : function(){
41634        Roo.util.Observable.prototype.purgeListeners.call(this);
41635        this.el.removeAllListeners();
41636     },
41637     /**
41638      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41639      */
41640     show : function(){
41641         this.status_node.addClass("active");
41642         this.showAction();
41643         if(Roo.isOpera){
41644             this.tabPanel.stripWrap.repaint();
41645         }
41646         this.fireEvent("activate", this.tabPanel, this);
41647     },
41648
41649     /**
41650      * Returns true if this tab is the active tab.
41651      * @return {Boolean}
41652      */
41653     isActive : function(){
41654         return this.tabPanel.getActiveTab() == this;
41655     },
41656
41657     /**
41658      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41659      */
41660     hide : function(){
41661         this.status_node.removeClass("active");
41662         this.hideAction();
41663         this.fireEvent("deactivate", this.tabPanel, this);
41664     },
41665
41666     hideAction : function(){
41667         this.bodyEl.hide();
41668         this.bodyEl.setStyle("position", "absolute");
41669         this.bodyEl.setLeft("-20000px");
41670         this.bodyEl.setTop("-20000px");
41671     },
41672
41673     showAction : function(){
41674         this.bodyEl.setStyle("position", "relative");
41675         this.bodyEl.setTop("");
41676         this.bodyEl.setLeft("");
41677         this.bodyEl.show();
41678     },
41679
41680     /**
41681      * Set the tooltip for the tab.
41682      * @param {String} tooltip The tab's tooltip
41683      */
41684     setTooltip : function(text){
41685         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41686             this.textEl.dom.qtip = text;
41687             this.textEl.dom.removeAttribute('title');
41688         }else{
41689             this.textEl.dom.title = text;
41690         }
41691     },
41692
41693     onTabClick : function(e){
41694         e.preventDefault();
41695         this.tabPanel.activate(this.id);
41696     },
41697
41698     onTabMouseDown : function(e){
41699         e.preventDefault();
41700         this.tabPanel.activate(this.id);
41701     },
41702 /*
41703     getWidth : function(){
41704         return this.inner.getWidth();
41705     },
41706
41707     setWidth : function(width){
41708         var iwidth = width - this.linode.getPadding("lr");
41709         this.inner.setWidth(iwidth);
41710         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41711         this.linode.setWidth(width);
41712     },
41713 */
41714     /**
41715      * Show or hide the tab
41716      * @param {Boolean} hidden True to hide or false to show.
41717      */
41718     setHidden : function(hidden){
41719         this.hidden = hidden;
41720         this.linode.setStyle("display", hidden ? "none" : "");
41721     },
41722
41723     /**
41724      * Returns true if this tab is "hidden"
41725      * @return {Boolean}
41726      */
41727     isHidden : function(){
41728         return this.hidden;
41729     },
41730
41731     /**
41732      * Returns the text for this tab
41733      * @return {String}
41734      */
41735     getText : function(){
41736         return this.text;
41737     },
41738     /*
41739     autoSize : function(){
41740         //this.el.beginMeasure();
41741         this.textEl.setWidth(1);
41742         /*
41743          *  #2804 [new] Tabs in Roojs
41744          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41745          */
41746         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41747         //this.el.endMeasure();
41748     //},
41749
41750     /**
41751      * Sets the text for the tab (Note: this also sets the tooltip text)
41752      * @param {String} text The tab's text and tooltip
41753      */
41754     setText : function(text){
41755         this.text = text;
41756         this.textEl.update(text);
41757         this.setTooltip(text);
41758         //if(!this.tabPanel.resizeTabs){
41759         //    this.autoSize();
41760         //}
41761     },
41762     /**
41763      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41764      */
41765     activate : function(){
41766         this.tabPanel.activate(this.id);
41767     },
41768
41769     /**
41770      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41771      */
41772     disable : function(){
41773         if(this.tabPanel.active != this){
41774             this.disabled = true;
41775             this.status_node.addClass("disabled");
41776         }
41777     },
41778
41779     /**
41780      * Enables this TabPanelItem if it was previously disabled.
41781      */
41782     enable : function(){
41783         this.disabled = false;
41784         this.status_node.removeClass("disabled");
41785     },
41786
41787     /**
41788      * Sets the content for this TabPanelItem.
41789      * @param {String} content The content
41790      * @param {Boolean} loadScripts true to look for and load scripts
41791      */
41792     setContent : function(content, loadScripts){
41793         this.bodyEl.update(content, loadScripts);
41794     },
41795
41796     /**
41797      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41798      * @return {Roo.UpdateManager} The UpdateManager
41799      */
41800     getUpdateManager : function(){
41801         return this.bodyEl.getUpdateManager();
41802     },
41803
41804     /**
41805      * Set a URL to be used to load the content for this TabPanelItem.
41806      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41807      * @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)
41808      * @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)
41809      * @return {Roo.UpdateManager} The UpdateManager
41810      */
41811     setUrl : function(url, params, loadOnce){
41812         if(this.refreshDelegate){
41813             this.un('activate', this.refreshDelegate);
41814         }
41815         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41816         this.on("activate", this.refreshDelegate);
41817         return this.bodyEl.getUpdateManager();
41818     },
41819
41820     /** @private */
41821     _handleRefresh : function(url, params, loadOnce){
41822         if(!loadOnce || !this.loaded){
41823             var updater = this.bodyEl.getUpdateManager();
41824             updater.update(url, params, this._setLoaded.createDelegate(this));
41825         }
41826     },
41827
41828     /**
41829      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41830      *   Will fail silently if the setUrl method has not been called.
41831      *   This does not activate the panel, just updates its content.
41832      */
41833     refresh : function(){
41834         if(this.refreshDelegate){
41835            this.loaded = false;
41836            this.refreshDelegate();
41837         }
41838     },
41839
41840     /** @private */
41841     _setLoaded : function(){
41842         this.loaded = true;
41843     },
41844
41845     /** @private */
41846     closeClick : function(e){
41847         var o = {};
41848         e.stopEvent();
41849         this.fireEvent("beforeclose", this, o);
41850         if(o.cancel !== true){
41851             this.tabPanel.removeTab(this.id);
41852         }
41853     },
41854     /**
41855      * The text displayed in the tooltip for the close icon.
41856      * @type String
41857      */
41858     closeText : "Close this tab"
41859 });
41860 /**
41861 *    This script refer to:
41862 *    Title: International Telephone Input
41863 *    Author: Jack O'Connor
41864 *    Code version:  v12.1.12
41865 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41866 **/
41867
41868 Roo.bootstrap.PhoneInputData = function() {
41869     var d = [
41870       [
41871         "Afghanistan (‫افغانستان‬‎)",
41872         "af",
41873         "93"
41874       ],
41875       [
41876         "Albania (Shqipëri)",
41877         "al",
41878         "355"
41879       ],
41880       [
41881         "Algeria (‫الجزائر‬‎)",
41882         "dz",
41883         "213"
41884       ],
41885       [
41886         "American Samoa",
41887         "as",
41888         "1684"
41889       ],
41890       [
41891         "Andorra",
41892         "ad",
41893         "376"
41894       ],
41895       [
41896         "Angola",
41897         "ao",
41898         "244"
41899       ],
41900       [
41901         "Anguilla",
41902         "ai",
41903         "1264"
41904       ],
41905       [
41906         "Antigua and Barbuda",
41907         "ag",
41908         "1268"
41909       ],
41910       [
41911         "Argentina",
41912         "ar",
41913         "54"
41914       ],
41915       [
41916         "Armenia (Հայաստան)",
41917         "am",
41918         "374"
41919       ],
41920       [
41921         "Aruba",
41922         "aw",
41923         "297"
41924       ],
41925       [
41926         "Australia",
41927         "au",
41928         "61",
41929         0
41930       ],
41931       [
41932         "Austria (Österreich)",
41933         "at",
41934         "43"
41935       ],
41936       [
41937         "Azerbaijan (Azərbaycan)",
41938         "az",
41939         "994"
41940       ],
41941       [
41942         "Bahamas",
41943         "bs",
41944         "1242"
41945       ],
41946       [
41947         "Bahrain (‫البحرين‬‎)",
41948         "bh",
41949         "973"
41950       ],
41951       [
41952         "Bangladesh (বাংলাদেশ)",
41953         "bd",
41954         "880"
41955       ],
41956       [
41957         "Barbados",
41958         "bb",
41959         "1246"
41960       ],
41961       [
41962         "Belarus (Беларусь)",
41963         "by",
41964         "375"
41965       ],
41966       [
41967         "Belgium (België)",
41968         "be",
41969         "32"
41970       ],
41971       [
41972         "Belize",
41973         "bz",
41974         "501"
41975       ],
41976       [
41977         "Benin (Bénin)",
41978         "bj",
41979         "229"
41980       ],
41981       [
41982         "Bermuda",
41983         "bm",
41984         "1441"
41985       ],
41986       [
41987         "Bhutan (འབྲུག)",
41988         "bt",
41989         "975"
41990       ],
41991       [
41992         "Bolivia",
41993         "bo",
41994         "591"
41995       ],
41996       [
41997         "Bosnia and Herzegovina (Босна и Херцеговина)",
41998         "ba",
41999         "387"
42000       ],
42001       [
42002         "Botswana",
42003         "bw",
42004         "267"
42005       ],
42006       [
42007         "Brazil (Brasil)",
42008         "br",
42009         "55"
42010       ],
42011       [
42012         "British Indian Ocean Territory",
42013         "io",
42014         "246"
42015       ],
42016       [
42017         "British Virgin Islands",
42018         "vg",
42019         "1284"
42020       ],
42021       [
42022         "Brunei",
42023         "bn",
42024         "673"
42025       ],
42026       [
42027         "Bulgaria (България)",
42028         "bg",
42029         "359"
42030       ],
42031       [
42032         "Burkina Faso",
42033         "bf",
42034         "226"
42035       ],
42036       [
42037         "Burundi (Uburundi)",
42038         "bi",
42039         "257"
42040       ],
42041       [
42042         "Cambodia (កម្ពុជា)",
42043         "kh",
42044         "855"
42045       ],
42046       [
42047         "Cameroon (Cameroun)",
42048         "cm",
42049         "237"
42050       ],
42051       [
42052         "Canada",
42053         "ca",
42054         "1",
42055         1,
42056         ["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"]
42057       ],
42058       [
42059         "Cape Verde (Kabu Verdi)",
42060         "cv",
42061         "238"
42062       ],
42063       [
42064         "Caribbean Netherlands",
42065         "bq",
42066         "599",
42067         1
42068       ],
42069       [
42070         "Cayman Islands",
42071         "ky",
42072         "1345"
42073       ],
42074       [
42075         "Central African Republic (République centrafricaine)",
42076         "cf",
42077         "236"
42078       ],
42079       [
42080         "Chad (Tchad)",
42081         "td",
42082         "235"
42083       ],
42084       [
42085         "Chile",
42086         "cl",
42087         "56"
42088       ],
42089       [
42090         "China (中国)",
42091         "cn",
42092         "86"
42093       ],
42094       [
42095         "Christmas Island",
42096         "cx",
42097         "61",
42098         2
42099       ],
42100       [
42101         "Cocos (Keeling) Islands",
42102         "cc",
42103         "61",
42104         1
42105       ],
42106       [
42107         "Colombia",
42108         "co",
42109         "57"
42110       ],
42111       [
42112         "Comoros (‫جزر القمر‬‎)",
42113         "km",
42114         "269"
42115       ],
42116       [
42117         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42118         "cd",
42119         "243"
42120       ],
42121       [
42122         "Congo (Republic) (Congo-Brazzaville)",
42123         "cg",
42124         "242"
42125       ],
42126       [
42127         "Cook Islands",
42128         "ck",
42129         "682"
42130       ],
42131       [
42132         "Costa Rica",
42133         "cr",
42134         "506"
42135       ],
42136       [
42137         "Côte d’Ivoire",
42138         "ci",
42139         "225"
42140       ],
42141       [
42142         "Croatia (Hrvatska)",
42143         "hr",
42144         "385"
42145       ],
42146       [
42147         "Cuba",
42148         "cu",
42149         "53"
42150       ],
42151       [
42152         "Curaçao",
42153         "cw",
42154         "599",
42155         0
42156       ],
42157       [
42158         "Cyprus (Κύπρος)",
42159         "cy",
42160         "357"
42161       ],
42162       [
42163         "Czech Republic (Česká republika)",
42164         "cz",
42165         "420"
42166       ],
42167       [
42168         "Denmark (Danmark)",
42169         "dk",
42170         "45"
42171       ],
42172       [
42173         "Djibouti",
42174         "dj",
42175         "253"
42176       ],
42177       [
42178         "Dominica",
42179         "dm",
42180         "1767"
42181       ],
42182       [
42183         "Dominican Republic (República Dominicana)",
42184         "do",
42185         "1",
42186         2,
42187         ["809", "829", "849"]
42188       ],
42189       [
42190         "Ecuador",
42191         "ec",
42192         "593"
42193       ],
42194       [
42195         "Egypt (‫مصر‬‎)",
42196         "eg",
42197         "20"
42198       ],
42199       [
42200         "El Salvador",
42201         "sv",
42202         "503"
42203       ],
42204       [
42205         "Equatorial Guinea (Guinea Ecuatorial)",
42206         "gq",
42207         "240"
42208       ],
42209       [
42210         "Eritrea",
42211         "er",
42212         "291"
42213       ],
42214       [
42215         "Estonia (Eesti)",
42216         "ee",
42217         "372"
42218       ],
42219       [
42220         "Ethiopia",
42221         "et",
42222         "251"
42223       ],
42224       [
42225         "Falkland Islands (Islas Malvinas)",
42226         "fk",
42227         "500"
42228       ],
42229       [
42230         "Faroe Islands (Føroyar)",
42231         "fo",
42232         "298"
42233       ],
42234       [
42235         "Fiji",
42236         "fj",
42237         "679"
42238       ],
42239       [
42240         "Finland (Suomi)",
42241         "fi",
42242         "358",
42243         0
42244       ],
42245       [
42246         "France",
42247         "fr",
42248         "33"
42249       ],
42250       [
42251         "French Guiana (Guyane française)",
42252         "gf",
42253         "594"
42254       ],
42255       [
42256         "French Polynesia (Polynésie française)",
42257         "pf",
42258         "689"
42259       ],
42260       [
42261         "Gabon",
42262         "ga",
42263         "241"
42264       ],
42265       [
42266         "Gambia",
42267         "gm",
42268         "220"
42269       ],
42270       [
42271         "Georgia (საქართველო)",
42272         "ge",
42273         "995"
42274       ],
42275       [
42276         "Germany (Deutschland)",
42277         "de",
42278         "49"
42279       ],
42280       [
42281         "Ghana (Gaana)",
42282         "gh",
42283         "233"
42284       ],
42285       [
42286         "Gibraltar",
42287         "gi",
42288         "350"
42289       ],
42290       [
42291         "Greece (Ελλάδα)",
42292         "gr",
42293         "30"
42294       ],
42295       [
42296         "Greenland (Kalaallit Nunaat)",
42297         "gl",
42298         "299"
42299       ],
42300       [
42301         "Grenada",
42302         "gd",
42303         "1473"
42304       ],
42305       [
42306         "Guadeloupe",
42307         "gp",
42308         "590",
42309         0
42310       ],
42311       [
42312         "Guam",
42313         "gu",
42314         "1671"
42315       ],
42316       [
42317         "Guatemala",
42318         "gt",
42319         "502"
42320       ],
42321       [
42322         "Guernsey",
42323         "gg",
42324         "44",
42325         1
42326       ],
42327       [
42328         "Guinea (Guinée)",
42329         "gn",
42330         "224"
42331       ],
42332       [
42333         "Guinea-Bissau (Guiné Bissau)",
42334         "gw",
42335         "245"
42336       ],
42337       [
42338         "Guyana",
42339         "gy",
42340         "592"
42341       ],
42342       [
42343         "Haiti",
42344         "ht",
42345         "509"
42346       ],
42347       [
42348         "Honduras",
42349         "hn",
42350         "504"
42351       ],
42352       [
42353         "Hong Kong (香港)",
42354         "hk",
42355         "852"
42356       ],
42357       [
42358         "Hungary (Magyarország)",
42359         "hu",
42360         "36"
42361       ],
42362       [
42363         "Iceland (Ísland)",
42364         "is",
42365         "354"
42366       ],
42367       [
42368         "India (भारत)",
42369         "in",
42370         "91"
42371       ],
42372       [
42373         "Indonesia",
42374         "id",
42375         "62"
42376       ],
42377       [
42378         "Iran (‫ایران‬‎)",
42379         "ir",
42380         "98"
42381       ],
42382       [
42383         "Iraq (‫العراق‬‎)",
42384         "iq",
42385         "964"
42386       ],
42387       [
42388         "Ireland",
42389         "ie",
42390         "353"
42391       ],
42392       [
42393         "Isle of Man",
42394         "im",
42395         "44",
42396         2
42397       ],
42398       [
42399         "Israel (‫ישראל‬‎)",
42400         "il",
42401         "972"
42402       ],
42403       [
42404         "Italy (Italia)",
42405         "it",
42406         "39",
42407         0
42408       ],
42409       [
42410         "Jamaica",
42411         "jm",
42412         "1876"
42413       ],
42414       [
42415         "Japan (日本)",
42416         "jp",
42417         "81"
42418       ],
42419       [
42420         "Jersey",
42421         "je",
42422         "44",
42423         3
42424       ],
42425       [
42426         "Jordan (‫الأردن‬‎)",
42427         "jo",
42428         "962"
42429       ],
42430       [
42431         "Kazakhstan (Казахстан)",
42432         "kz",
42433         "7",
42434         1
42435       ],
42436       [
42437         "Kenya",
42438         "ke",
42439         "254"
42440       ],
42441       [
42442         "Kiribati",
42443         "ki",
42444         "686"
42445       ],
42446       [
42447         "Kosovo",
42448         "xk",
42449         "383"
42450       ],
42451       [
42452         "Kuwait (‫الكويت‬‎)",
42453         "kw",
42454         "965"
42455       ],
42456       [
42457         "Kyrgyzstan (Кыргызстан)",
42458         "kg",
42459         "996"
42460       ],
42461       [
42462         "Laos (ລາວ)",
42463         "la",
42464         "856"
42465       ],
42466       [
42467         "Latvia (Latvija)",
42468         "lv",
42469         "371"
42470       ],
42471       [
42472         "Lebanon (‫لبنان‬‎)",
42473         "lb",
42474         "961"
42475       ],
42476       [
42477         "Lesotho",
42478         "ls",
42479         "266"
42480       ],
42481       [
42482         "Liberia",
42483         "lr",
42484         "231"
42485       ],
42486       [
42487         "Libya (‫ليبيا‬‎)",
42488         "ly",
42489         "218"
42490       ],
42491       [
42492         "Liechtenstein",
42493         "li",
42494         "423"
42495       ],
42496       [
42497         "Lithuania (Lietuva)",
42498         "lt",
42499         "370"
42500       ],
42501       [
42502         "Luxembourg",
42503         "lu",
42504         "352"
42505       ],
42506       [
42507         "Macau (澳門)",
42508         "mo",
42509         "853"
42510       ],
42511       [
42512         "Macedonia (FYROM) (Македонија)",
42513         "mk",
42514         "389"
42515       ],
42516       [
42517         "Madagascar (Madagasikara)",
42518         "mg",
42519         "261"
42520       ],
42521       [
42522         "Malawi",
42523         "mw",
42524         "265"
42525       ],
42526       [
42527         "Malaysia",
42528         "my",
42529         "60"
42530       ],
42531       [
42532         "Maldives",
42533         "mv",
42534         "960"
42535       ],
42536       [
42537         "Mali",
42538         "ml",
42539         "223"
42540       ],
42541       [
42542         "Malta",
42543         "mt",
42544         "356"
42545       ],
42546       [
42547         "Marshall Islands",
42548         "mh",
42549         "692"
42550       ],
42551       [
42552         "Martinique",
42553         "mq",
42554         "596"
42555       ],
42556       [
42557         "Mauritania (‫موريتانيا‬‎)",
42558         "mr",
42559         "222"
42560       ],
42561       [
42562         "Mauritius (Moris)",
42563         "mu",
42564         "230"
42565       ],
42566       [
42567         "Mayotte",
42568         "yt",
42569         "262",
42570         1
42571       ],
42572       [
42573         "Mexico (México)",
42574         "mx",
42575         "52"
42576       ],
42577       [
42578         "Micronesia",
42579         "fm",
42580         "691"
42581       ],
42582       [
42583         "Moldova (Republica Moldova)",
42584         "md",
42585         "373"
42586       ],
42587       [
42588         "Monaco",
42589         "mc",
42590         "377"
42591       ],
42592       [
42593         "Mongolia (Монгол)",
42594         "mn",
42595         "976"
42596       ],
42597       [
42598         "Montenegro (Crna Gora)",
42599         "me",
42600         "382"
42601       ],
42602       [
42603         "Montserrat",
42604         "ms",
42605         "1664"
42606       ],
42607       [
42608         "Morocco (‫المغرب‬‎)",
42609         "ma",
42610         "212",
42611         0
42612       ],
42613       [
42614         "Mozambique (Moçambique)",
42615         "mz",
42616         "258"
42617       ],
42618       [
42619         "Myanmar (Burma) (မြန်မာ)",
42620         "mm",
42621         "95"
42622       ],
42623       [
42624         "Namibia (Namibië)",
42625         "na",
42626         "264"
42627       ],
42628       [
42629         "Nauru",
42630         "nr",
42631         "674"
42632       ],
42633       [
42634         "Nepal (नेपाल)",
42635         "np",
42636         "977"
42637       ],
42638       [
42639         "Netherlands (Nederland)",
42640         "nl",
42641         "31"
42642       ],
42643       [
42644         "New Caledonia (Nouvelle-Calédonie)",
42645         "nc",
42646         "687"
42647       ],
42648       [
42649         "New Zealand",
42650         "nz",
42651         "64"
42652       ],
42653       [
42654         "Nicaragua",
42655         "ni",
42656         "505"
42657       ],
42658       [
42659         "Niger (Nijar)",
42660         "ne",
42661         "227"
42662       ],
42663       [
42664         "Nigeria",
42665         "ng",
42666         "234"
42667       ],
42668       [
42669         "Niue",
42670         "nu",
42671         "683"
42672       ],
42673       [
42674         "Norfolk Island",
42675         "nf",
42676         "672"
42677       ],
42678       [
42679         "North Korea (조선 민주주의 인민 공화국)",
42680         "kp",
42681         "850"
42682       ],
42683       [
42684         "Northern Mariana Islands",
42685         "mp",
42686         "1670"
42687       ],
42688       [
42689         "Norway (Norge)",
42690         "no",
42691         "47",
42692         0
42693       ],
42694       [
42695         "Oman (‫عُمان‬‎)",
42696         "om",
42697         "968"
42698       ],
42699       [
42700         "Pakistan (‫پاکستان‬‎)",
42701         "pk",
42702         "92"
42703       ],
42704       [
42705         "Palau",
42706         "pw",
42707         "680"
42708       ],
42709       [
42710         "Palestine (‫فلسطين‬‎)",
42711         "ps",
42712         "970"
42713       ],
42714       [
42715         "Panama (Panamá)",
42716         "pa",
42717         "507"
42718       ],
42719       [
42720         "Papua New Guinea",
42721         "pg",
42722         "675"
42723       ],
42724       [
42725         "Paraguay",
42726         "py",
42727         "595"
42728       ],
42729       [
42730         "Peru (Perú)",
42731         "pe",
42732         "51"
42733       ],
42734       [
42735         "Philippines",
42736         "ph",
42737         "63"
42738       ],
42739       [
42740         "Poland (Polska)",
42741         "pl",
42742         "48"
42743       ],
42744       [
42745         "Portugal",
42746         "pt",
42747         "351"
42748       ],
42749       [
42750         "Puerto Rico",
42751         "pr",
42752         "1",
42753         3,
42754         ["787", "939"]
42755       ],
42756       [
42757         "Qatar (‫قطر‬‎)",
42758         "qa",
42759         "974"
42760       ],
42761       [
42762         "Réunion (La Réunion)",
42763         "re",
42764         "262",
42765         0
42766       ],
42767       [
42768         "Romania (România)",
42769         "ro",
42770         "40"
42771       ],
42772       [
42773         "Russia (Россия)",
42774         "ru",
42775         "7",
42776         0
42777       ],
42778       [
42779         "Rwanda",
42780         "rw",
42781         "250"
42782       ],
42783       [
42784         "Saint Barthélemy",
42785         "bl",
42786         "590",
42787         1
42788       ],
42789       [
42790         "Saint Helena",
42791         "sh",
42792         "290"
42793       ],
42794       [
42795         "Saint Kitts and Nevis",
42796         "kn",
42797         "1869"
42798       ],
42799       [
42800         "Saint Lucia",
42801         "lc",
42802         "1758"
42803       ],
42804       [
42805         "Saint Martin (Saint-Martin (partie française))",
42806         "mf",
42807         "590",
42808         2
42809       ],
42810       [
42811         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42812         "pm",
42813         "508"
42814       ],
42815       [
42816         "Saint Vincent and the Grenadines",
42817         "vc",
42818         "1784"
42819       ],
42820       [
42821         "Samoa",
42822         "ws",
42823         "685"
42824       ],
42825       [
42826         "San Marino",
42827         "sm",
42828         "378"
42829       ],
42830       [
42831         "São Tomé and Príncipe (São Tomé e Príncipe)",
42832         "st",
42833         "239"
42834       ],
42835       [
42836         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42837         "sa",
42838         "966"
42839       ],
42840       [
42841         "Senegal (Sénégal)",
42842         "sn",
42843         "221"
42844       ],
42845       [
42846         "Serbia (Србија)",
42847         "rs",
42848         "381"
42849       ],
42850       [
42851         "Seychelles",
42852         "sc",
42853         "248"
42854       ],
42855       [
42856         "Sierra Leone",
42857         "sl",
42858         "232"
42859       ],
42860       [
42861         "Singapore",
42862         "sg",
42863         "65"
42864       ],
42865       [
42866         "Sint Maarten",
42867         "sx",
42868         "1721"
42869       ],
42870       [
42871         "Slovakia (Slovensko)",
42872         "sk",
42873         "421"
42874       ],
42875       [
42876         "Slovenia (Slovenija)",
42877         "si",
42878         "386"
42879       ],
42880       [
42881         "Solomon Islands",
42882         "sb",
42883         "677"
42884       ],
42885       [
42886         "Somalia (Soomaaliya)",
42887         "so",
42888         "252"
42889       ],
42890       [
42891         "South Africa",
42892         "za",
42893         "27"
42894       ],
42895       [
42896         "South Korea (대한민국)",
42897         "kr",
42898         "82"
42899       ],
42900       [
42901         "South Sudan (‫جنوب السودان‬‎)",
42902         "ss",
42903         "211"
42904       ],
42905       [
42906         "Spain (España)",
42907         "es",
42908         "34"
42909       ],
42910       [
42911         "Sri Lanka (ශ්‍රී ලංකාව)",
42912         "lk",
42913         "94"
42914       ],
42915       [
42916         "Sudan (‫السودان‬‎)",
42917         "sd",
42918         "249"
42919       ],
42920       [
42921         "Suriname",
42922         "sr",
42923         "597"
42924       ],
42925       [
42926         "Svalbard and Jan Mayen",
42927         "sj",
42928         "47",
42929         1
42930       ],
42931       [
42932         "Swaziland",
42933         "sz",
42934         "268"
42935       ],
42936       [
42937         "Sweden (Sverige)",
42938         "se",
42939         "46"
42940       ],
42941       [
42942         "Switzerland (Schweiz)",
42943         "ch",
42944         "41"
42945       ],
42946       [
42947         "Syria (‫سوريا‬‎)",
42948         "sy",
42949         "963"
42950       ],
42951       [
42952         "Taiwan (台灣)",
42953         "tw",
42954         "886"
42955       ],
42956       [
42957         "Tajikistan",
42958         "tj",
42959         "992"
42960       ],
42961       [
42962         "Tanzania",
42963         "tz",
42964         "255"
42965       ],
42966       [
42967         "Thailand (ไทย)",
42968         "th",
42969         "66"
42970       ],
42971       [
42972         "Timor-Leste",
42973         "tl",
42974         "670"
42975       ],
42976       [
42977         "Togo",
42978         "tg",
42979         "228"
42980       ],
42981       [
42982         "Tokelau",
42983         "tk",
42984         "690"
42985       ],
42986       [
42987         "Tonga",
42988         "to",
42989         "676"
42990       ],
42991       [
42992         "Trinidad and Tobago",
42993         "tt",
42994         "1868"
42995       ],
42996       [
42997         "Tunisia (‫تونس‬‎)",
42998         "tn",
42999         "216"
43000       ],
43001       [
43002         "Turkey (Türkiye)",
43003         "tr",
43004         "90"
43005       ],
43006       [
43007         "Turkmenistan",
43008         "tm",
43009         "993"
43010       ],
43011       [
43012         "Turks and Caicos Islands",
43013         "tc",
43014         "1649"
43015       ],
43016       [
43017         "Tuvalu",
43018         "tv",
43019         "688"
43020       ],
43021       [
43022         "U.S. Virgin Islands",
43023         "vi",
43024         "1340"
43025       ],
43026       [
43027         "Uganda",
43028         "ug",
43029         "256"
43030       ],
43031       [
43032         "Ukraine (Україна)",
43033         "ua",
43034         "380"
43035       ],
43036       [
43037         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43038         "ae",
43039         "971"
43040       ],
43041       [
43042         "United Kingdom",
43043         "gb",
43044         "44",
43045         0
43046       ],
43047       [
43048         "United States",
43049         "us",
43050         "1",
43051         0
43052       ],
43053       [
43054         "Uruguay",
43055         "uy",
43056         "598"
43057       ],
43058       [
43059         "Uzbekistan (Oʻzbekiston)",
43060         "uz",
43061         "998"
43062       ],
43063       [
43064         "Vanuatu",
43065         "vu",
43066         "678"
43067       ],
43068       [
43069         "Vatican City (Città del Vaticano)",
43070         "va",
43071         "39",
43072         1
43073       ],
43074       [
43075         "Venezuela",
43076         "ve",
43077         "58"
43078       ],
43079       [
43080         "Vietnam (Việt Nam)",
43081         "vn",
43082         "84"
43083       ],
43084       [
43085         "Wallis and Futuna (Wallis-et-Futuna)",
43086         "wf",
43087         "681"
43088       ],
43089       [
43090         "Western Sahara (‫الصحراء الغربية‬‎)",
43091         "eh",
43092         "212",
43093         1
43094       ],
43095       [
43096         "Yemen (‫اليمن‬‎)",
43097         "ye",
43098         "967"
43099       ],
43100       [
43101         "Zambia",
43102         "zm",
43103         "260"
43104       ],
43105       [
43106         "Zimbabwe",
43107         "zw",
43108         "263"
43109       ],
43110       [
43111         "Åland Islands",
43112         "ax",
43113         "358",
43114         1
43115       ]
43116   ];
43117   
43118   return d;
43119 }/**
43120 *    This script refer to:
43121 *    Title: International Telephone Input
43122 *    Author: Jack O'Connor
43123 *    Code version:  v12.1.12
43124 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43125 **/
43126
43127 /**
43128  * @class Roo.bootstrap.PhoneInput
43129  * @extends Roo.bootstrap.TriggerField
43130  * An input with International dial-code selection
43131  
43132  * @cfg {String} defaultDialCode default '+852'
43133  * @cfg {Array} preferedCountries default []
43134   
43135  * @constructor
43136  * Create a new PhoneInput.
43137  * @param {Object} config Configuration options
43138  */
43139
43140 Roo.bootstrap.PhoneInput = function(config) {
43141     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43142 };
43143
43144 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43145         
43146         listWidth: undefined,
43147         
43148         selectedClass: 'active',
43149         
43150         invalidClass : "has-warning",
43151         
43152         validClass: 'has-success',
43153         
43154         allowed: '0123456789',
43155         
43156         max_length: 15,
43157         
43158         /**
43159          * @cfg {String} defaultDialCode The default dial code when initializing the input
43160          */
43161         defaultDialCode: '+852',
43162         
43163         /**
43164          * @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
43165          */
43166         preferedCountries: false,
43167         
43168         getAutoCreate : function()
43169         {
43170             var data = Roo.bootstrap.PhoneInputData();
43171             var align = this.labelAlign || this.parentLabelAlign();
43172             var id = Roo.id();
43173             
43174             this.allCountries = [];
43175             this.dialCodeMapping = [];
43176             
43177             for (var i = 0; i < data.length; i++) {
43178               var c = data[i];
43179               this.allCountries[i] = {
43180                 name: c[0],
43181                 iso2: c[1],
43182                 dialCode: c[2],
43183                 priority: c[3] || 0,
43184                 areaCodes: c[4] || null
43185               };
43186               this.dialCodeMapping[c[2]] = {
43187                   name: c[0],
43188                   iso2: c[1],
43189                   priority: c[3] || 0,
43190                   areaCodes: c[4] || null
43191               };
43192             }
43193             
43194             var cfg = {
43195                 cls: 'form-group',
43196                 cn: []
43197             };
43198             
43199             var input =  {
43200                 tag: 'input',
43201                 id : id,
43202                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43203                 maxlength: this.max_length,
43204                 cls : 'form-control tel-input',
43205                 autocomplete: 'new-password'
43206             };
43207             
43208             var hiddenInput = {
43209                 tag: 'input',
43210                 type: 'hidden',
43211                 cls: 'hidden-tel-input'
43212             };
43213             
43214             if (this.name) {
43215                 hiddenInput.name = this.name;
43216             }
43217             
43218             if (this.disabled) {
43219                 input.disabled = true;
43220             }
43221             
43222             var flag_container = {
43223                 tag: 'div',
43224                 cls: 'flag-box',
43225                 cn: [
43226                     {
43227                         tag: 'div',
43228                         cls: 'flag'
43229                     },
43230                     {
43231                         tag: 'div',
43232                         cls: 'caret'
43233                     }
43234                 ]
43235             };
43236             
43237             var box = {
43238                 tag: 'div',
43239                 cls: this.hasFeedback ? 'has-feedback' : '',
43240                 cn: [
43241                     hiddenInput,
43242                     input,
43243                     {
43244                         tag: 'input',
43245                         cls: 'dial-code-holder',
43246                         disabled: true
43247                     }
43248                 ]
43249             };
43250             
43251             var container = {
43252                 cls: 'roo-select2-container input-group',
43253                 cn: [
43254                     flag_container,
43255                     box
43256                 ]
43257             };
43258             
43259             if (this.fieldLabel.length) {
43260                 var indicator = {
43261                     tag: 'i',
43262                     tooltip: 'This field is required'
43263                 };
43264                 
43265                 var label = {
43266                     tag: 'label',
43267                     'for':  id,
43268                     cls: 'control-label',
43269                     cn: []
43270                 };
43271                 
43272                 var label_text = {
43273                     tag: 'span',
43274                     html: this.fieldLabel
43275                 };
43276                 
43277                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43278                 label.cn = [
43279                     indicator,
43280                     label_text
43281                 ];
43282                 
43283                 if(this.indicatorpos == 'right') {
43284                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43285                     label.cn = [
43286                         label_text,
43287                         indicator
43288                     ];
43289                 }
43290                 
43291                 if(align == 'left') {
43292                     container = {
43293                         tag: 'div',
43294                         cn: [
43295                             container
43296                         ]
43297                     };
43298                     
43299                     if(this.labelWidth > 12){
43300                         label.style = "width: " + this.labelWidth + 'px';
43301                     }
43302                     if(this.labelWidth < 13 && this.labelmd == 0){
43303                         this.labelmd = this.labelWidth;
43304                     }
43305                     if(this.labellg > 0){
43306                         label.cls += ' col-lg-' + this.labellg;
43307                         input.cls += ' col-lg-' + (12 - this.labellg);
43308                     }
43309                     if(this.labelmd > 0){
43310                         label.cls += ' col-md-' + this.labelmd;
43311                         container.cls += ' col-md-' + (12 - this.labelmd);
43312                     }
43313                     if(this.labelsm > 0){
43314                         label.cls += ' col-sm-' + this.labelsm;
43315                         container.cls += ' col-sm-' + (12 - this.labelsm);
43316                     }
43317                     if(this.labelxs > 0){
43318                         label.cls += ' col-xs-' + this.labelxs;
43319                         container.cls += ' col-xs-' + (12 - this.labelxs);
43320                     }
43321                 }
43322             }
43323             
43324             cfg.cn = [
43325                 label,
43326                 container
43327             ];
43328             
43329             var settings = this;
43330             
43331             ['xs','sm','md','lg'].map(function(size){
43332                 if (settings[size]) {
43333                     cfg.cls += ' col-' + size + '-' + settings[size];
43334                 }
43335             });
43336             
43337             this.store = new Roo.data.Store({
43338                 proxy : new Roo.data.MemoryProxy({}),
43339                 reader : new Roo.data.JsonReader({
43340                     fields : [
43341                         {
43342                             'name' : 'name',
43343                             'type' : 'string'
43344                         },
43345                         {
43346                             'name' : 'iso2',
43347                             'type' : 'string'
43348                         },
43349                         {
43350                             'name' : 'dialCode',
43351                             'type' : 'string'
43352                         },
43353                         {
43354                             'name' : 'priority',
43355                             'type' : 'string'
43356                         },
43357                         {
43358                             'name' : 'areaCodes',
43359                             'type' : 'string'
43360                         }
43361                     ]
43362                 })
43363             });
43364             
43365             if(!this.preferedCountries) {
43366                 this.preferedCountries = [
43367                     'hk',
43368                     'gb',
43369                     'us'
43370                 ];
43371             }
43372             
43373             var p = this.preferedCountries.reverse();
43374             
43375             if(p) {
43376                 for (var i = 0; i < p.length; i++) {
43377                     for (var j = 0; j < this.allCountries.length; j++) {
43378                         if(this.allCountries[j].iso2 == p[i]) {
43379                             var t = this.allCountries[j];
43380                             this.allCountries.splice(j,1);
43381                             this.allCountries.unshift(t);
43382                         }
43383                     } 
43384                 }
43385             }
43386             
43387             this.store.proxy.data = {
43388                 success: true,
43389                 data: this.allCountries
43390             };
43391             
43392             return cfg;
43393         },
43394         
43395         initEvents : function()
43396         {
43397             this.createList();
43398             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43399             
43400             this.indicator = this.indicatorEl();
43401             this.flag = this.flagEl();
43402             this.dialCodeHolder = this.dialCodeHolderEl();
43403             
43404             this.trigger = this.el.select('div.flag-box',true).first();
43405             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43406             
43407             var _this = this;
43408             
43409             (function(){
43410                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43411                 _this.list.setWidth(lw);
43412             }).defer(100);
43413             
43414             this.list.on('mouseover', this.onViewOver, this);
43415             this.list.on('mousemove', this.onViewMove, this);
43416             this.inputEl().on("keyup", this.onKeyUp, this);
43417             this.inputEl().on("keypress", this.onKeyPress, this);
43418             
43419             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43420
43421             this.view = new Roo.View(this.list, this.tpl, {
43422                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43423             });
43424             
43425             this.view.on('click', this.onViewClick, this);
43426             this.setValue(this.defaultDialCode);
43427         },
43428         
43429         onTriggerClick : function(e)
43430         {
43431             Roo.log('trigger click');
43432             if(this.disabled){
43433                 return;
43434             }
43435             
43436             if(this.isExpanded()){
43437                 this.collapse();
43438                 this.hasFocus = false;
43439             }else {
43440                 this.store.load({});
43441                 this.hasFocus = true;
43442                 this.expand();
43443             }
43444         },
43445         
43446         isExpanded : function()
43447         {
43448             return this.list.isVisible();
43449         },
43450         
43451         collapse : function()
43452         {
43453             if(!this.isExpanded()){
43454                 return;
43455             }
43456             this.list.hide();
43457             Roo.get(document).un('mousedown', this.collapseIf, this);
43458             Roo.get(document).un('mousewheel', this.collapseIf, this);
43459             this.fireEvent('collapse', this);
43460             this.validate();
43461         },
43462         
43463         expand : function()
43464         {
43465             Roo.log('expand');
43466
43467             if(this.isExpanded() || !this.hasFocus){
43468                 return;
43469             }
43470             
43471             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43472             this.list.setWidth(lw);
43473             
43474             this.list.show();
43475             this.restrictHeight();
43476             
43477             Roo.get(document).on('mousedown', this.collapseIf, this);
43478             Roo.get(document).on('mousewheel', this.collapseIf, this);
43479             
43480             this.fireEvent('expand', this);
43481         },
43482         
43483         restrictHeight : function()
43484         {
43485             this.list.alignTo(this.inputEl(), this.listAlign);
43486             this.list.alignTo(this.inputEl(), this.listAlign);
43487         },
43488         
43489         onViewOver : function(e, t)
43490         {
43491             if(this.inKeyMode){
43492                 return;
43493             }
43494             var item = this.view.findItemFromChild(t);
43495             
43496             if(item){
43497                 var index = this.view.indexOf(item);
43498                 this.select(index, false);
43499             }
43500         },
43501
43502         // private
43503         onViewClick : function(view, doFocus, el, e)
43504         {
43505             var index = this.view.getSelectedIndexes()[0];
43506             
43507             var r = this.store.getAt(index);
43508             
43509             if(r){
43510                 this.onSelect(r, index);
43511             }
43512             if(doFocus !== false && !this.blockFocus){
43513                 this.inputEl().focus();
43514             }
43515         },
43516         
43517         onViewMove : function(e, t)
43518         {
43519             this.inKeyMode = false;
43520         },
43521         
43522         select : function(index, scrollIntoView)
43523         {
43524             this.selectedIndex = index;
43525             this.view.select(index);
43526             if(scrollIntoView !== false){
43527                 var el = this.view.getNode(index);
43528                 if(el){
43529                     this.list.scrollChildIntoView(el, false);
43530                 }
43531             }
43532         },
43533         
43534         createList : function()
43535         {
43536             this.list = Roo.get(document.body).createChild({
43537                 tag: 'ul',
43538                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43539                 style: 'display:none'
43540             });
43541             
43542             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43543         },
43544         
43545         collapseIf : function(e)
43546         {
43547             var in_combo  = e.within(this.el);
43548             var in_list =  e.within(this.list);
43549             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43550             
43551             if (in_combo || in_list || is_list) {
43552                 return;
43553             }
43554             this.collapse();
43555         },
43556         
43557         onSelect : function(record, index)
43558         {
43559             if(this.fireEvent('beforeselect', this, record, index) !== false){
43560                 
43561                 this.setFlagClass(record.data.iso2);
43562                 this.setDialCode(record.data.dialCode);
43563                 this.hasFocus = false;
43564                 this.collapse();
43565                 this.fireEvent('select', this, record, index);
43566             }
43567         },
43568         
43569         flagEl : function()
43570         {
43571             var flag = this.el.select('div.flag',true).first();
43572             if(!flag){
43573                 return false;
43574             }
43575             return flag;
43576         },
43577         
43578         dialCodeHolderEl : function()
43579         {
43580             var d = this.el.select('input.dial-code-holder',true).first();
43581             if(!d){
43582                 return false;
43583             }
43584             return d;
43585         },
43586         
43587         setDialCode : function(v)
43588         {
43589             this.dialCodeHolder.dom.value = '+'+v;
43590         },
43591         
43592         setFlagClass : function(n)
43593         {
43594             this.flag.dom.className = 'flag '+n;
43595         },
43596         
43597         getValue : function()
43598         {
43599             var v = this.inputEl().getValue();
43600             if(this.dialCodeHolder) {
43601                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43602             }
43603             return v;
43604         },
43605         
43606         setValue : function(v)
43607         {
43608             var d = this.getDialCode(v);
43609             
43610             //invalid dial code
43611             if(v.length == 0 || !d || d.length == 0) {
43612                 if(this.rendered){
43613                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43614                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43615                 }
43616                 return;
43617             }
43618             
43619             //valid dial code
43620             this.setFlagClass(this.dialCodeMapping[d].iso2);
43621             this.setDialCode(d);
43622             this.inputEl().dom.value = v.replace('+'+d,'');
43623             this.hiddenEl().dom.value = this.getValue();
43624             
43625             this.validate();
43626         },
43627         
43628         getDialCode : function(v)
43629         {
43630             v = v ||  '';
43631             
43632             if (v.length == 0) {
43633                 return this.dialCodeHolder.dom.value;
43634             }
43635             
43636             var dialCode = "";
43637             if (v.charAt(0) != "+") {
43638                 return false;
43639             }
43640             var numericChars = "";
43641             for (var i = 1; i < v.length; i++) {
43642               var c = v.charAt(i);
43643               if (!isNaN(c)) {
43644                 numericChars += c;
43645                 if (this.dialCodeMapping[numericChars]) {
43646                   dialCode = v.substr(1, i);
43647                 }
43648                 if (numericChars.length == 4) {
43649                   break;
43650                 }
43651               }
43652             }
43653             return dialCode;
43654         },
43655         
43656         reset : function()
43657         {
43658             this.setValue(this.defaultDialCode);
43659             this.validate();
43660         },
43661         
43662         hiddenEl : function()
43663         {
43664             return this.el.select('input.hidden-tel-input',true).first();
43665         },
43666         
43667         // after setting val
43668         onKeyUp : function(e){
43669             this.setValue(this.getValue());
43670         },
43671         
43672         onKeyPress : function(e){
43673             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43674                 e.stopEvent();
43675             }
43676         }
43677         
43678 });
43679 /**
43680  * @class Roo.bootstrap.MoneyField
43681  * @extends Roo.bootstrap.ComboBox
43682  * Bootstrap MoneyField class
43683  * 
43684  * @constructor
43685  * Create a new MoneyField.
43686  * @param {Object} config Configuration options
43687  */
43688
43689 Roo.bootstrap.MoneyField = function(config) {
43690     
43691     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43692     
43693 };
43694
43695 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43696     
43697     /**
43698      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43699      */
43700     allowDecimals : true,
43701     /**
43702      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43703      */
43704     decimalSeparator : ".",
43705     /**
43706      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43707      */
43708     decimalPrecision : 0,
43709     /**
43710      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43711      */
43712     allowNegative : true,
43713     /**
43714      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43715      */
43716     allowZero: true,
43717     /**
43718      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43719      */
43720     minValue : Number.NEGATIVE_INFINITY,
43721     /**
43722      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43723      */
43724     maxValue : Number.MAX_VALUE,
43725     /**
43726      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43727      */
43728     minText : "The minimum value for this field is {0}",
43729     /**
43730      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43731      */
43732     maxText : "The maximum value for this field is {0}",
43733     /**
43734      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43735      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43736      */
43737     nanText : "{0} is not a valid number",
43738     /**
43739      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43740      */
43741     castInt : true,
43742     /**
43743      * @cfg {String} defaults currency of the MoneyField
43744      * value should be in lkey
43745      */
43746     defaultCurrency : false,
43747     /**
43748      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43749      */
43750     thousandsDelimiter : false,
43751     /**
43752      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43753      */
43754     max_length: false,
43755     
43756     inputlg : 9,
43757     inputmd : 9,
43758     inputsm : 9,
43759     inputxs : 6,
43760     
43761     store : false,
43762     
43763     getAutoCreate : function()
43764     {
43765         var align = this.labelAlign || this.parentLabelAlign();
43766         
43767         var id = Roo.id();
43768
43769         var cfg = {
43770             cls: 'form-group',
43771             cn: []
43772         };
43773
43774         var input =  {
43775             tag: 'input',
43776             id : id,
43777             cls : 'form-control roo-money-amount-input',
43778             autocomplete: 'new-password'
43779         };
43780         
43781         var hiddenInput = {
43782             tag: 'input',
43783             type: 'hidden',
43784             id: Roo.id(),
43785             cls: 'hidden-number-input'
43786         };
43787         
43788         if(this.max_length) {
43789             input.maxlength = this.max_length; 
43790         }
43791         
43792         if (this.name) {
43793             hiddenInput.name = this.name;
43794         }
43795
43796         if (this.disabled) {
43797             input.disabled = true;
43798         }
43799
43800         var clg = 12 - this.inputlg;
43801         var cmd = 12 - this.inputmd;
43802         var csm = 12 - this.inputsm;
43803         var cxs = 12 - this.inputxs;
43804         
43805         var container = {
43806             tag : 'div',
43807             cls : 'row roo-money-field',
43808             cn : [
43809                 {
43810                     tag : 'div',
43811                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43812                     cn : [
43813                         {
43814                             tag : 'div',
43815                             cls: 'roo-select2-container input-group',
43816                             cn: [
43817                                 {
43818                                     tag : 'input',
43819                                     cls : 'form-control roo-money-currency-input',
43820                                     autocomplete: 'new-password',
43821                                     readOnly : 1,
43822                                     name : this.currencyName
43823                                 },
43824                                 {
43825                                     tag :'span',
43826                                     cls : 'input-group-addon',
43827                                     cn : [
43828                                         {
43829                                             tag: 'span',
43830                                             cls: 'caret'
43831                                         }
43832                                     ]
43833                                 }
43834                             ]
43835                         }
43836                     ]
43837                 },
43838                 {
43839                     tag : 'div',
43840                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43841                     cn : [
43842                         {
43843                             tag: 'div',
43844                             cls: this.hasFeedback ? 'has-feedback' : '',
43845                             cn: [
43846                                 input
43847                             ]
43848                         }
43849                     ]
43850                 }
43851             ]
43852             
43853         };
43854         
43855         if (this.fieldLabel.length) {
43856             var indicator = {
43857                 tag: 'i',
43858                 tooltip: 'This field is required'
43859             };
43860
43861             var label = {
43862                 tag: 'label',
43863                 'for':  id,
43864                 cls: 'control-label',
43865                 cn: []
43866             };
43867
43868             var label_text = {
43869                 tag: 'span',
43870                 html: this.fieldLabel
43871             };
43872
43873             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43874             label.cn = [
43875                 indicator,
43876                 label_text
43877             ];
43878
43879             if(this.indicatorpos == 'right') {
43880                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43881                 label.cn = [
43882                     label_text,
43883                     indicator
43884                 ];
43885             }
43886
43887             if(align == 'left') {
43888                 container = {
43889                     tag: 'div',
43890                     cn: [
43891                         container
43892                     ]
43893                 };
43894
43895                 if(this.labelWidth > 12){
43896                     label.style = "width: " + this.labelWidth + 'px';
43897                 }
43898                 if(this.labelWidth < 13 && this.labelmd == 0){
43899                     this.labelmd = this.labelWidth;
43900                 }
43901                 if(this.labellg > 0){
43902                     label.cls += ' col-lg-' + this.labellg;
43903                     input.cls += ' col-lg-' + (12 - this.labellg);
43904                 }
43905                 if(this.labelmd > 0){
43906                     label.cls += ' col-md-' + this.labelmd;
43907                     container.cls += ' col-md-' + (12 - this.labelmd);
43908                 }
43909                 if(this.labelsm > 0){
43910                     label.cls += ' col-sm-' + this.labelsm;
43911                     container.cls += ' col-sm-' + (12 - this.labelsm);
43912                 }
43913                 if(this.labelxs > 0){
43914                     label.cls += ' col-xs-' + this.labelxs;
43915                     container.cls += ' col-xs-' + (12 - this.labelxs);
43916                 }
43917             }
43918         }
43919
43920         cfg.cn = [
43921             label,
43922             container,
43923             hiddenInput
43924         ];
43925         
43926         var settings = this;
43927
43928         ['xs','sm','md','lg'].map(function(size){
43929             if (settings[size]) {
43930                 cfg.cls += ' col-' + size + '-' + settings[size];
43931             }
43932         });
43933         
43934         return cfg;
43935     },
43936     
43937     initEvents : function()
43938     {
43939         this.indicator = this.indicatorEl();
43940         
43941         this.initCurrencyEvent();
43942         
43943         this.initNumberEvent();
43944     },
43945     
43946     initCurrencyEvent : function()
43947     {
43948         if (!this.store) {
43949             throw "can not find store for combo";
43950         }
43951         
43952         this.store = Roo.factory(this.store, Roo.data);
43953         this.store.parent = this;
43954         
43955         this.createList();
43956         
43957         this.triggerEl = this.el.select('.input-group-addon', true).first();
43958         
43959         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43960         
43961         var _this = this;
43962         
43963         (function(){
43964             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43965             _this.list.setWidth(lw);
43966         }).defer(100);
43967         
43968         this.list.on('mouseover', this.onViewOver, this);
43969         this.list.on('mousemove', this.onViewMove, this);
43970         this.list.on('scroll', this.onViewScroll, this);
43971         
43972         if(!this.tpl){
43973             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43974         }
43975         
43976         this.view = new Roo.View(this.list, this.tpl, {
43977             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43978         });
43979         
43980         this.view.on('click', this.onViewClick, this);
43981         
43982         this.store.on('beforeload', this.onBeforeLoad, this);
43983         this.store.on('load', this.onLoad, this);
43984         this.store.on('loadexception', this.onLoadException, this);
43985         
43986         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43987             "up" : function(e){
43988                 this.inKeyMode = true;
43989                 this.selectPrev();
43990             },
43991
43992             "down" : function(e){
43993                 if(!this.isExpanded()){
43994                     this.onTriggerClick();
43995                 }else{
43996                     this.inKeyMode = true;
43997                     this.selectNext();
43998                 }
43999             },
44000
44001             "enter" : function(e){
44002                 this.collapse();
44003                 
44004                 if(this.fireEvent("specialkey", this, e)){
44005                     this.onViewClick(false);
44006                 }
44007                 
44008                 return true;
44009             },
44010
44011             "esc" : function(e){
44012                 this.collapse();
44013             },
44014
44015             "tab" : function(e){
44016                 this.collapse();
44017                 
44018                 if(this.fireEvent("specialkey", this, e)){
44019                     this.onViewClick(false);
44020                 }
44021                 
44022                 return true;
44023             },
44024
44025             scope : this,
44026
44027             doRelay : function(foo, bar, hname){
44028                 if(hname == 'down' || this.scope.isExpanded()){
44029                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44030                 }
44031                 return true;
44032             },
44033
44034             forceKeyDown: true
44035         });
44036         
44037         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44038         
44039     },
44040     
44041     initNumberEvent : function(e)
44042     {
44043         this.inputEl().on("keydown" , this.fireKey,  this);
44044         this.inputEl().on("focus", this.onFocus,  this);
44045         this.inputEl().on("blur", this.onBlur,  this);
44046         
44047         this.inputEl().relayEvent('keyup', this);
44048         
44049         if(this.indicator){
44050             this.indicator.addClass('invisible');
44051         }
44052  
44053         this.originalValue = this.getValue();
44054         
44055         if(this.validationEvent == 'keyup'){
44056             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44057             this.inputEl().on('keyup', this.filterValidation, this);
44058         }
44059         else if(this.validationEvent !== false){
44060             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44061         }
44062         
44063         if(this.selectOnFocus){
44064             this.on("focus", this.preFocus, this);
44065             
44066         }
44067         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44068             this.inputEl().on("keypress", this.filterKeys, this);
44069         } else {
44070             this.inputEl().relayEvent('keypress', this);
44071         }
44072         
44073         var allowed = "0123456789";
44074         
44075         if(this.allowDecimals){
44076             allowed += this.decimalSeparator;
44077         }
44078         
44079         if(this.allowNegative){
44080             allowed += "-";
44081         }
44082         
44083         if(this.thousandsDelimiter) {
44084             allowed += ",";
44085         }
44086         
44087         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44088         
44089         var keyPress = function(e){
44090             
44091             var k = e.getKey();
44092             
44093             var c = e.getCharCode();
44094             
44095             if(
44096                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44097                     allowed.indexOf(String.fromCharCode(c)) === -1
44098             ){
44099                 e.stopEvent();
44100                 return;
44101             }
44102             
44103             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44104                 return;
44105             }
44106             
44107             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44108                 e.stopEvent();
44109             }
44110         };
44111         
44112         this.inputEl().on("keypress", keyPress, this);
44113         
44114     },
44115     
44116     onTriggerClick : function(e)
44117     {   
44118         if(this.disabled){
44119             return;
44120         }
44121         
44122         this.page = 0;
44123         this.loadNext = false;
44124         
44125         if(this.isExpanded()){
44126             this.collapse();
44127             return;
44128         }
44129         
44130         this.hasFocus = true;
44131         
44132         if(this.triggerAction == 'all') {
44133             this.doQuery(this.allQuery, true);
44134             return;
44135         }
44136         
44137         this.doQuery(this.getRawValue());
44138     },
44139     
44140     getCurrency : function()
44141     {   
44142         var v = this.currencyEl().getValue();
44143         
44144         return v;
44145     },
44146     
44147     restrictHeight : function()
44148     {
44149         this.list.alignTo(this.currencyEl(), this.listAlign);
44150         this.list.alignTo(this.currencyEl(), this.listAlign);
44151     },
44152     
44153     onViewClick : function(view, doFocus, el, e)
44154     {
44155         var index = this.view.getSelectedIndexes()[0];
44156         
44157         var r = this.store.getAt(index);
44158         
44159         if(r){
44160             this.onSelect(r, index);
44161         }
44162     },
44163     
44164     onSelect : function(record, index){
44165         
44166         if(this.fireEvent('beforeselect', this, record, index) !== false){
44167         
44168             this.setFromCurrencyData(index > -1 ? record.data : false);
44169             
44170             this.collapse();
44171             
44172             this.fireEvent('select', this, record, index);
44173         }
44174     },
44175     
44176     setFromCurrencyData : function(o)
44177     {
44178         var currency = '';
44179         
44180         this.lastCurrency = o;
44181         
44182         if (this.currencyField) {
44183             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44184         } else {
44185             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44186         }
44187         
44188         this.lastSelectionText = currency;
44189         
44190         //setting default currency
44191         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44192             this.setCurrency(this.defaultCurrency);
44193             return;
44194         }
44195         
44196         this.setCurrency(currency);
44197     },
44198     
44199     setFromData : function(o)
44200     {
44201         var c = {};
44202         
44203         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44204         
44205         this.setFromCurrencyData(c);
44206         
44207         var value = '';
44208         
44209         if (this.name) {
44210             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44211         } else {
44212             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44213         }
44214         
44215         this.setValue(value);
44216         
44217     },
44218     
44219     setCurrency : function(v)
44220     {   
44221         this.currencyValue = v;
44222         
44223         if(this.rendered){
44224             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44225             this.validate();
44226         }
44227     },
44228     
44229     setValue : function(v)
44230     {
44231         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44232         
44233         this.value = v;
44234         
44235         if(this.rendered){
44236             
44237             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44238             
44239             this.inputEl().dom.value = (v == '') ? '' :
44240                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44241             
44242             if(!this.allowZero && v === '0') {
44243                 this.hiddenEl().dom.value = '';
44244                 this.inputEl().dom.value = '';
44245             }
44246             
44247             this.validate();
44248         }
44249     },
44250     
44251     getRawValue : function()
44252     {
44253         var v = this.inputEl().getValue();
44254         
44255         return v;
44256     },
44257     
44258     getValue : function()
44259     {
44260         return this.fixPrecision(this.parseValue(this.getRawValue()));
44261     },
44262     
44263     parseValue : function(value)
44264     {
44265         if(this.thousandsDelimiter) {
44266             value += "";
44267             r = new RegExp(",", "g");
44268             value = value.replace(r, "");
44269         }
44270         
44271         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44272         return isNaN(value) ? '' : value;
44273         
44274     },
44275     
44276     fixPrecision : function(value)
44277     {
44278         if(this.thousandsDelimiter) {
44279             value += "";
44280             r = new RegExp(",", "g");
44281             value = value.replace(r, "");
44282         }
44283         
44284         var nan = isNaN(value);
44285         
44286         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44287             return nan ? '' : value;
44288         }
44289         return parseFloat(value).toFixed(this.decimalPrecision);
44290     },
44291     
44292     decimalPrecisionFcn : function(v)
44293     {
44294         return Math.floor(v);
44295     },
44296     
44297     validateValue : function(value)
44298     {
44299         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44300             return false;
44301         }
44302         
44303         var num = this.parseValue(value);
44304         
44305         if(isNaN(num)){
44306             this.markInvalid(String.format(this.nanText, value));
44307             return false;
44308         }
44309         
44310         if(num < this.minValue){
44311             this.markInvalid(String.format(this.minText, this.minValue));
44312             return false;
44313         }
44314         
44315         if(num > this.maxValue){
44316             this.markInvalid(String.format(this.maxText, this.maxValue));
44317             return false;
44318         }
44319         
44320         return true;
44321     },
44322     
44323     validate : function()
44324     {
44325         if(this.disabled || this.allowBlank){
44326             this.markValid();
44327             return true;
44328         }
44329         
44330         var currency = this.getCurrency();
44331         
44332         if(this.validateValue(this.getRawValue()) && currency.length){
44333             this.markValid();
44334             return true;
44335         }
44336         
44337         this.markInvalid();
44338         return false;
44339     },
44340     
44341     getName: function()
44342     {
44343         return this.name;
44344     },
44345     
44346     beforeBlur : function()
44347     {
44348         if(!this.castInt){
44349             return;
44350         }
44351         
44352         var v = this.parseValue(this.getRawValue());
44353         
44354         if(v || v == 0){
44355             this.setValue(v);
44356         }
44357     },
44358     
44359     onBlur : function()
44360     {
44361         this.beforeBlur();
44362         
44363         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44364             //this.el.removeClass(this.focusClass);
44365         }
44366         
44367         this.hasFocus = false;
44368         
44369         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44370             this.validate();
44371         }
44372         
44373         var v = this.getValue();
44374         
44375         if(String(v) !== String(this.startValue)){
44376             this.fireEvent('change', this, v, this.startValue);
44377         }
44378         
44379         this.fireEvent("blur", this);
44380     },
44381     
44382     inputEl : function()
44383     {
44384         return this.el.select('.roo-money-amount-input', true).first();
44385     },
44386     
44387     currencyEl : function()
44388     {
44389         return this.el.select('.roo-money-currency-input', true).first();
44390     },
44391     
44392     hiddenEl : function()
44393     {
44394         return this.el.select('input.hidden-number-input',true).first();
44395     }
44396     
44397 });/**
44398  * @class Roo.bootstrap.BezierSignature
44399  * @extends Roo.bootstrap.Component
44400  * Bootstrap BezierSignature class
44401  * This script refer to:
44402  *    Title: Signature Pad
44403  *    Author: szimek
44404  *    Availability: https://github.com/szimek/signature_pad
44405  *
44406  * @constructor
44407  * Create a new BezierSignature
44408  * @param {Object} config The config object
44409  */
44410
44411 Roo.bootstrap.BezierSignature = function(config){
44412     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44413     this.addEvents({
44414         "resize" : true
44415     });
44416 };
44417
44418 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44419 {
44420      
44421     curve_data: [],
44422     
44423     is_empty: true,
44424     
44425     mouse_btn_down: true,
44426     
44427     /**
44428      * @cfg {int} canvas height
44429      */
44430     canvas_height: '200px',
44431     
44432     /**
44433      * @cfg {float|function} Radius of a single dot.
44434      */ 
44435     dot_size: false,
44436     
44437     /**
44438      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44439      */
44440     min_width: 0.5,
44441     
44442     /**
44443      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44444      */
44445     max_width: 2.5,
44446     
44447     /**
44448      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44449      */
44450     throttle: 16,
44451     
44452     /**
44453      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44454      */
44455     min_distance: 5,
44456     
44457     /**
44458      * @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.
44459      */
44460     bg_color: 'rgba(0, 0, 0, 0)',
44461     
44462     /**
44463      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44464      */
44465     dot_color: 'black',
44466     
44467     /**
44468      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44469      */ 
44470     velocity_filter_weight: 0.7,
44471     
44472     /**
44473      * @cfg {function} Callback when stroke begin. 
44474      */
44475     onBegin: false,
44476     
44477     /**
44478      * @cfg {function} Callback when stroke end.
44479      */
44480     onEnd: false,
44481     
44482     getAutoCreate : function()
44483     {
44484         var cls = 'roo-signature column';
44485         
44486         if(this.cls){
44487             cls += ' ' + this.cls;
44488         }
44489         
44490         var col_sizes = [
44491             'lg',
44492             'md',
44493             'sm',
44494             'xs'
44495         ];
44496         
44497         for(var i = 0; i < col_sizes.length; i++) {
44498             if(this[col_sizes[i]]) {
44499                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44500             }
44501         }
44502         
44503         var cfg = {
44504             tag: 'div',
44505             cls: cls,
44506             cn: [
44507                 {
44508                     tag: 'div',
44509                     cls: 'roo-signature-body',
44510                     cn: [
44511                         {
44512                             tag: 'canvas',
44513                             cls: 'roo-signature-body-canvas',
44514                             height: this.canvas_height,
44515                             width: this.canvas_width
44516                         }
44517                     ]
44518                 },
44519                 {
44520                     tag: 'input',
44521                     type: 'file',
44522                     style: 'display: none'
44523                 }
44524             ]
44525         };
44526         
44527         return cfg;
44528     },
44529     
44530     initEvents: function() 
44531     {
44532         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44533         
44534         var canvas = this.canvasEl();
44535         
44536         // mouse && touch event swapping...
44537         canvas.dom.style.touchAction = 'none';
44538         canvas.dom.style.msTouchAction = 'none';
44539         
44540         this.mouse_btn_down = false;
44541         canvas.on('mousedown', this._handleMouseDown, this);
44542         canvas.on('mousemove', this._handleMouseMove, this);
44543         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44544         
44545         if (window.PointerEvent) {
44546             canvas.on('pointerdown', this._handleMouseDown, this);
44547             canvas.on('pointermove', this._handleMouseMove, this);
44548             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44549         }
44550         
44551         if ('ontouchstart' in window) {
44552             canvas.on('touchstart', this._handleTouchStart, this);
44553             canvas.on('touchmove', this._handleTouchMove, this);
44554             canvas.on('touchend', this._handleTouchEnd, this);
44555         }
44556         
44557         Roo.EventManager.onWindowResize(this.resize, this, true);
44558         
44559         // file input event
44560         this.fileEl().on('change', this.uploadImage, this);
44561         
44562         this.clear();
44563         
44564         this.resize();
44565     },
44566     
44567     resize: function(){
44568         
44569         var canvas = this.canvasEl().dom;
44570         var ctx = this.canvasElCtx();
44571         var img_data = false;
44572         
44573         if(canvas.width > 0) {
44574             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44575         }
44576         // setting canvas width will clean img data
44577         canvas.width = 0;
44578         
44579         var style = window.getComputedStyle ? 
44580             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44581             
44582         var padding_left = parseInt(style.paddingLeft) || 0;
44583         var padding_right = parseInt(style.paddingRight) || 0;
44584         
44585         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44586         
44587         if(img_data) {
44588             ctx.putImageData(img_data, 0, 0);
44589         }
44590     },
44591     
44592     _handleMouseDown: function(e)
44593     {
44594         if (e.browserEvent.which === 1) {
44595             this.mouse_btn_down = true;
44596             this.strokeBegin(e);
44597         }
44598     },
44599     
44600     _handleMouseMove: function (e)
44601     {
44602         if (this.mouse_btn_down) {
44603             this.strokeMoveUpdate(e);
44604         }
44605     },
44606     
44607     _handleMouseUp: function (e)
44608     {
44609         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44610             this.mouse_btn_down = false;
44611             this.strokeEnd(e);
44612         }
44613     },
44614     
44615     _handleTouchStart: function (e) {
44616         
44617         e.preventDefault();
44618         if (e.browserEvent.targetTouches.length === 1) {
44619             // var touch = e.browserEvent.changedTouches[0];
44620             // this.strokeBegin(touch);
44621             
44622              this.strokeBegin(e); // assume e catching the correct xy...
44623         }
44624     },
44625     
44626     _handleTouchMove: function (e) {
44627         e.preventDefault();
44628         // var touch = event.targetTouches[0];
44629         // _this._strokeMoveUpdate(touch);
44630         this.strokeMoveUpdate(e);
44631     },
44632     
44633     _handleTouchEnd: function (e) {
44634         var wasCanvasTouched = e.target === this.canvasEl().dom;
44635         if (wasCanvasTouched) {
44636             e.preventDefault();
44637             // var touch = event.changedTouches[0];
44638             // _this._strokeEnd(touch);
44639             this.strokeEnd(e);
44640         }
44641     },
44642     
44643     reset: function () {
44644         this._lastPoints = [];
44645         this._lastVelocity = 0;
44646         this._lastWidth = (this.min_width + this.max_width) / 2;
44647         this.canvasElCtx().fillStyle = this.dot_color;
44648     },
44649     
44650     strokeMoveUpdate: function(e)
44651     {
44652         this.strokeUpdate(e);
44653         
44654         if (this.throttle) {
44655             this.throttleStroke(this.strokeUpdate, this.throttle);
44656         }
44657         else {
44658             this.strokeUpdate(e);
44659         }
44660     },
44661     
44662     strokeBegin: function(e)
44663     {
44664         var newPointGroup = {
44665             color: this.dot_color,
44666             points: []
44667         };
44668         
44669         if (typeof this.onBegin === 'function') {
44670             this.onBegin(e);
44671         }
44672         
44673         this.curve_data.push(newPointGroup);
44674         this.reset();
44675         this.strokeUpdate(e);
44676     },
44677     
44678     strokeUpdate: function(e)
44679     {
44680         var rect = this.canvasEl().dom.getBoundingClientRect();
44681         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44682         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44683         var lastPoints = lastPointGroup.points;
44684         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44685         var isLastPointTooClose = lastPoint
44686             ? point.distanceTo(lastPoint) <= this.min_distance
44687             : false;
44688         var color = lastPointGroup.color;
44689         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44690             var curve = this.addPoint(point);
44691             if (!lastPoint) {
44692                 this.drawDot({color: color, point: point});
44693             }
44694             else if (curve) {
44695                 this.drawCurve({color: color, curve: curve});
44696             }
44697             lastPoints.push({
44698                 time: point.time,
44699                 x: point.x,
44700                 y: point.y
44701             });
44702         }
44703     },
44704     
44705     strokeEnd: function(e)
44706     {
44707         this.strokeUpdate(e);
44708         if (typeof this.onEnd === 'function') {
44709             this.onEnd(e);
44710         }
44711     },
44712     
44713     addPoint:  function (point) {
44714         var _lastPoints = this._lastPoints;
44715         _lastPoints.push(point);
44716         if (_lastPoints.length > 2) {
44717             if (_lastPoints.length === 3) {
44718                 _lastPoints.unshift(_lastPoints[0]);
44719             }
44720             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44721             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44722             _lastPoints.shift();
44723             return curve;
44724         }
44725         return null;
44726     },
44727     
44728     calculateCurveWidths: function (startPoint, endPoint) {
44729         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44730             (1 - this.velocity_filter_weight) * this._lastVelocity;
44731
44732         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44733         var widths = {
44734             end: newWidth,
44735             start: this._lastWidth
44736         };
44737         
44738         this._lastVelocity = velocity;
44739         this._lastWidth = newWidth;
44740         return widths;
44741     },
44742     
44743     drawDot: function (_a) {
44744         var color = _a.color, point = _a.point;
44745         var ctx = this.canvasElCtx();
44746         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44747         ctx.beginPath();
44748         this.drawCurveSegment(point.x, point.y, width);
44749         ctx.closePath();
44750         ctx.fillStyle = color;
44751         ctx.fill();
44752     },
44753     
44754     drawCurve: function (_a) {
44755         var color = _a.color, curve = _a.curve;
44756         var ctx = this.canvasElCtx();
44757         var widthDelta = curve.endWidth - curve.startWidth;
44758         var drawSteps = Math.floor(curve.length()) * 2;
44759         ctx.beginPath();
44760         ctx.fillStyle = color;
44761         for (var i = 0; i < drawSteps; i += 1) {
44762         var t = i / drawSteps;
44763         var tt = t * t;
44764         var ttt = tt * t;
44765         var u = 1 - t;
44766         var uu = u * u;
44767         var uuu = uu * u;
44768         var x = uuu * curve.startPoint.x;
44769         x += 3 * uu * t * curve.control1.x;
44770         x += 3 * u * tt * curve.control2.x;
44771         x += ttt * curve.endPoint.x;
44772         var y = uuu * curve.startPoint.y;
44773         y += 3 * uu * t * curve.control1.y;
44774         y += 3 * u * tt * curve.control2.y;
44775         y += ttt * curve.endPoint.y;
44776         var width = curve.startWidth + ttt * widthDelta;
44777         this.drawCurveSegment(x, y, width);
44778         }
44779         ctx.closePath();
44780         ctx.fill();
44781     },
44782     
44783     drawCurveSegment: function (x, y, width) {
44784         var ctx = this.canvasElCtx();
44785         ctx.moveTo(x, y);
44786         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44787         this.is_empty = false;
44788     },
44789     
44790     clear: function()
44791     {
44792         var ctx = this.canvasElCtx();
44793         var canvas = this.canvasEl().dom;
44794         ctx.fillStyle = this.bg_color;
44795         ctx.clearRect(0, 0, canvas.width, canvas.height);
44796         ctx.fillRect(0, 0, canvas.width, canvas.height);
44797         this.curve_data = [];
44798         this.reset();
44799         this.is_empty = true;
44800     },
44801     
44802     fileEl: function()
44803     {
44804         return  this.el.select('input',true).first();
44805     },
44806     
44807     canvasEl: function()
44808     {
44809         return this.el.select('canvas',true).first();
44810     },
44811     
44812     canvasElCtx: function()
44813     {
44814         return this.el.select('canvas',true).first().dom.getContext('2d');
44815     },
44816     
44817     getImage: function(type)
44818     {
44819         if(this.is_empty) {
44820             return false;
44821         }
44822         
44823         // encryption ?
44824         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44825     },
44826     
44827     drawFromImage: function(img_src)
44828     {
44829         var img = new Image();
44830         
44831         img.onload = function(){
44832             this.canvasElCtx().drawImage(img, 0, 0);
44833         }.bind(this);
44834         
44835         img.src = img_src;
44836         
44837         this.is_empty = false;
44838     },
44839     
44840     selectImage: function()
44841     {
44842         this.fileEl().dom.click();
44843     },
44844     
44845     uploadImage: function(e)
44846     {
44847         var reader = new FileReader();
44848         
44849         reader.onload = function(e){
44850             var img = new Image();
44851             img.onload = function(){
44852                 this.reset();
44853                 this.canvasElCtx().drawImage(img, 0, 0);
44854             }.bind(this);
44855             img.src = e.target.result;
44856         }.bind(this);
44857         
44858         reader.readAsDataURL(e.target.files[0]);
44859     },
44860     
44861     // Bezier Point Constructor
44862     Point: (function () {
44863         function Point(x, y, time) {
44864             this.x = x;
44865             this.y = y;
44866             this.time = time || Date.now();
44867         }
44868         Point.prototype.distanceTo = function (start) {
44869             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44870         };
44871         Point.prototype.equals = function (other) {
44872             return this.x === other.x && this.y === other.y && this.time === other.time;
44873         };
44874         Point.prototype.velocityFrom = function (start) {
44875             return this.time !== start.time
44876             ? this.distanceTo(start) / (this.time - start.time)
44877             : 0;
44878         };
44879         return Point;
44880     }()),
44881     
44882     
44883     // Bezier Constructor
44884     Bezier: (function () {
44885         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44886             this.startPoint = startPoint;
44887             this.control2 = control2;
44888             this.control1 = control1;
44889             this.endPoint = endPoint;
44890             this.startWidth = startWidth;
44891             this.endWidth = endWidth;
44892         }
44893         Bezier.fromPoints = function (points, widths, scope) {
44894             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44895             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44896             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44897         };
44898         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44899             var dx1 = s1.x - s2.x;
44900             var dy1 = s1.y - s2.y;
44901             var dx2 = s2.x - s3.x;
44902             var dy2 = s2.y - s3.y;
44903             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44904             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44905             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44906             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44907             var dxm = m1.x - m2.x;
44908             var dym = m1.y - m2.y;
44909             var k = l2 / (l1 + l2);
44910             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44911             var tx = s2.x - cm.x;
44912             var ty = s2.y - cm.y;
44913             return {
44914                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44915                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44916             };
44917         };
44918         Bezier.prototype.length = function () {
44919             var steps = 10;
44920             var length = 0;
44921             var px;
44922             var py;
44923             for (var i = 0; i <= steps; i += 1) {
44924                 var t = i / steps;
44925                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44926                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44927                 if (i > 0) {
44928                     var xdiff = cx - px;
44929                     var ydiff = cy - py;
44930                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44931                 }
44932                 px = cx;
44933                 py = cy;
44934             }
44935             return length;
44936         };
44937         Bezier.prototype.point = function (t, start, c1, c2, end) {
44938             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44939             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44940             + (3.0 * c2 * (1.0 - t) * t * t)
44941             + (end * t * t * t);
44942         };
44943         return Bezier;
44944     }()),
44945     
44946     throttleStroke: function(fn, wait) {
44947       if (wait === void 0) { wait = 250; }
44948       var previous = 0;
44949       var timeout = null;
44950       var result;
44951       var storedContext;
44952       var storedArgs;
44953       var later = function () {
44954           previous = Date.now();
44955           timeout = null;
44956           result = fn.apply(storedContext, storedArgs);
44957           if (!timeout) {
44958               storedContext = null;
44959               storedArgs = [];
44960           }
44961       };
44962       return function wrapper() {
44963           var args = [];
44964           for (var _i = 0; _i < arguments.length; _i++) {
44965               args[_i] = arguments[_i];
44966           }
44967           var now = Date.now();
44968           var remaining = wait - (now - previous);
44969           storedContext = this;
44970           storedArgs = args;
44971           if (remaining <= 0 || remaining > wait) {
44972               if (timeout) {
44973                   clearTimeout(timeout);
44974                   timeout = null;
44975               }
44976               previous = now;
44977               result = fn.apply(storedContext, storedArgs);
44978               if (!timeout) {
44979                   storedContext = null;
44980                   storedArgs = [];
44981               }
44982           }
44983           else if (!timeout) {
44984               timeout = window.setTimeout(later, remaining);
44985           }
44986           return result;
44987       };
44988   }
44989   
44990 });
44991
44992  
44993
44994