table selection
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     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  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = []; //config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         this.addColumn(config[i]);
7316         
7317     }
7318
7319     /**
7320      * The width of columns which have no width specified (defaults to 100)
7321      * @type Number
7322      */
7323     this.defaultWidth = 100;
7324
7325     /**
7326      * Default sortable of columns which have no sortable specified (defaults to false)
7327      * @type Boolean
7328      */
7329     this.defaultSortable = false;
7330
7331     this.addEvents({
7332         /**
7333              * @event widthchange
7334              * Fires when the width of a column changes.
7335              * @param {ColumnModel} this
7336              * @param {Number} columnIndex The column index
7337              * @param {Number} newWidth The new width
7338              */
7339             "widthchange": true,
7340         /**
7341              * @event headerchange
7342              * Fires when the text of a header changes.
7343              * @param {ColumnModel} this
7344              * @param {Number} columnIndex The column index
7345              * @param {Number} newText The new header text
7346              */
7347             "headerchange": true,
7348         /**
7349              * @event hiddenchange
7350              * Fires when a column is hidden or "unhidden".
7351              * @param {ColumnModel} this
7352              * @param {Number} columnIndex The column index
7353              * @param {Boolean} hidden true if hidden, false otherwise
7354              */
7355             "hiddenchange": true,
7356             /**
7357          * @event columnmoved
7358          * Fires when a column is moved.
7359          * @param {ColumnModel} this
7360          * @param {Number} oldIndex
7361          * @param {Number} newIndex
7362          */
7363         "columnmoved" : true,
7364         /**
7365          * @event columlockchange
7366          * Fires when a column's locked state is changed
7367          * @param {ColumnModel} this
7368          * @param {Number} colIndex
7369          * @param {Boolean} locked true if locked
7370          */
7371         "columnlockchange" : true
7372     });
7373     Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 };
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376     /**
7377      * @cfg {String} header The header text to display in the Grid view.
7378      */
7379     /**
7380      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382      * specified, the column's index is used as an index into the Record's data Array.
7383      */
7384     /**
7385      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7387      */
7388     /**
7389      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390      * Defaults to the value of the {@link #defaultSortable} property.
7391      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7392      */
7393     /**
7394      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7395      */
7396     /**
7397      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7398      */
7399     /**
7400      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7401      */
7402     /**
7403      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7404      */
7405     /**
7406      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7410      */
7411        /**
7412      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7413      */
7414     /**
7415      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7416      */
7417     /**
7418      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7419      */
7420     /**
7421      * @cfg {String} cursor (Optional)
7422      */
7423     /**
7424      * @cfg {String} tooltip (Optional)
7425      */
7426     /**
7427      * @cfg {Number} xs (Optional)
7428      */
7429     /**
7430      * @cfg {Number} sm (Optional)
7431      */
7432     /**
7433      * @cfg {Number} md (Optional)
7434      */
7435     /**
7436      * @cfg {Number} lg (Optional)
7437      */
7438     /**
7439      * Returns the id of the column at the specified index.
7440      * @param {Number} index The column index
7441      * @return {String} the id
7442      */
7443     getColumnId : function(index){
7444         return this.config[index].id;
7445     },
7446
7447     /**
7448      * Returns the column for a specified id.
7449      * @param {String} id The column id
7450      * @return {Object} the column
7451      */
7452     getColumnById : function(id){
7453         return this.lookup[id];
7454     },
7455
7456     
7457     /**
7458      * Returns the column Object for a specified dataIndex.
7459      * @param {String} dataIndex The column dataIndex
7460      * @return {Object|Boolean} the column or false if not found
7461      */
7462     getColumnByDataIndex: function(dataIndex){
7463         var index = this.findColumnIndex(dataIndex);
7464         return index > -1 ? this.config[index] : false;
7465     },
7466     
7467     /**
7468      * Returns the index for a specified column id.
7469      * @param {String} id The column id
7470      * @return {Number} the index, or -1 if not found
7471      */
7472     getIndexById : function(id){
7473         for(var i = 0, len = this.config.length; i < len; i++){
7474             if(this.config[i].id == id){
7475                 return i;
7476             }
7477         }
7478         return -1;
7479     },
7480     
7481     /**
7482      * Returns the index for a specified column dataIndex.
7483      * @param {String} dataIndex The column dataIndex
7484      * @return {Number} the index, or -1 if not found
7485      */
7486     
7487     findColumnIndex : function(dataIndex){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].dataIndex == dataIndex){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     
7497     moveColumn : function(oldIndex, newIndex){
7498         var c = this.config[oldIndex];
7499         this.config.splice(oldIndex, 1);
7500         this.config.splice(newIndex, 0, c);
7501         this.dataMap = null;
7502         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7503     },
7504
7505     isLocked : function(colIndex){
7506         return this.config[colIndex].locked === true;
7507     },
7508
7509     setLocked : function(colIndex, value, suppressEvent){
7510         if(this.isLocked(colIndex) == value){
7511             return;
7512         }
7513         this.config[colIndex].locked = value;
7514         if(!suppressEvent){
7515             this.fireEvent("columnlockchange", this, colIndex, value);
7516         }
7517     },
7518
7519     getTotalLockedWidth : function(){
7520         var totalWidth = 0;
7521         for(var i = 0; i < this.config.length; i++){
7522             if(this.isLocked(i) && !this.isHidden(i)){
7523                 this.totalWidth += this.getColumnWidth(i);
7524             }
7525         }
7526         return totalWidth;
7527     },
7528
7529     getLockedCount : function(){
7530         for(var i = 0, len = this.config.length; i < len; i++){
7531             if(!this.isLocked(i)){
7532                 return i;
7533             }
7534         }
7535         
7536         return this.config.length;
7537     },
7538
7539     /**
7540      * Returns the number of columns.
7541      * @return {Number}
7542      */
7543     getColumnCount : function(visibleOnly){
7544         if(visibleOnly === true){
7545             var c = 0;
7546             for(var i = 0, len = this.config.length; i < len; i++){
7547                 if(!this.isHidden(i)){
7548                     c++;
7549                 }
7550             }
7551             return c;
7552         }
7553         return this.config.length;
7554     },
7555
7556     /**
7557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558      * @param {Function} fn
7559      * @param {Object} scope (optional)
7560      * @return {Array} result
7561      */
7562     getColumnsBy : function(fn, scope){
7563         var r = [];
7564         for(var i = 0, len = this.config.length; i < len; i++){
7565             var c = this.config[i];
7566             if(fn.call(scope||this, c, i) === true){
7567                 r[r.length] = c;
7568             }
7569         }
7570         return r;
7571     },
7572
7573     /**
7574      * Returns true if the specified column is sortable.
7575      * @param {Number} col The column index
7576      * @return {Boolean}
7577      */
7578     isSortable : function(col){
7579         if(typeof this.config[col].sortable == "undefined"){
7580             return this.defaultSortable;
7581         }
7582         return this.config[col].sortable;
7583     },
7584
7585     /**
7586      * Returns the rendering (formatting) function defined for the column.
7587      * @param {Number} col The column index.
7588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589      */
7590     getRenderer : function(col){
7591         if(!this.config[col].renderer){
7592             return Roo.grid.ColumnModel.defaultRenderer;
7593         }
7594         return this.config[col].renderer;
7595     },
7596
7597     /**
7598      * Sets the rendering (formatting) function for a column.
7599      * @param {Number} col The column index
7600      * @param {Function} fn The function to use to process the cell's raw data
7601      * to return HTML markup for the grid view. The render function is called with
7602      * the following parameters:<ul>
7603      * <li>Data value.</li>
7604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605      * <li>css A CSS style string to apply to the table cell.</li>
7606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608      * <li>Row index</li>
7609      * <li>Column index</li>
7610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611      */
7612     setRenderer : function(col, fn){
7613         this.config[col].renderer = fn;
7614     },
7615
7616     /**
7617      * Returns the width for the specified column.
7618      * @param {Number} col The column index
7619      * @return {Number}
7620      */
7621     getColumnWidth : function(col){
7622         return this.config[col].width * 1 || this.defaultWidth;
7623     },
7624
7625     /**
7626      * Sets the width for a column.
7627      * @param {Number} col The column index
7628      * @param {Number} width The new width
7629      */
7630     setColumnWidth : function(col, width, suppressEvent){
7631         this.config[col].width = width;
7632         this.totalWidth = null;
7633         if(!suppressEvent){
7634              this.fireEvent("widthchange", this, col, width);
7635         }
7636     },
7637
7638     /**
7639      * Returns the total width of all columns.
7640      * @param {Boolean} includeHidden True to include hidden column widths
7641      * @return {Number}
7642      */
7643     getTotalWidth : function(includeHidden){
7644         if(!this.totalWidth){
7645             this.totalWidth = 0;
7646             for(var i = 0, len = this.config.length; i < len; i++){
7647                 if(includeHidden || !this.isHidden(i)){
7648                     this.totalWidth += this.getColumnWidth(i);
7649                 }
7650             }
7651         }
7652         return this.totalWidth;
7653     },
7654
7655     /**
7656      * Returns the header for the specified column.
7657      * @param {Number} col The column index
7658      * @return {String}
7659      */
7660     getColumnHeader : function(col){
7661         return this.config[col].header;
7662     },
7663
7664     /**
7665      * Sets the header for a column.
7666      * @param {Number} col The column index
7667      * @param {String} header The new header
7668      */
7669     setColumnHeader : function(col, header){
7670         this.config[col].header = header;
7671         this.fireEvent("headerchange", this, col, header);
7672     },
7673
7674     /**
7675      * Returns the tooltip for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnTooltip : function(col){
7680             return this.config[col].tooltip;
7681     },
7682     /**
7683      * Sets the tooltip for a column.
7684      * @param {Number} col The column index
7685      * @param {String} tooltip The new tooltip
7686      */
7687     setColumnTooltip : function(col, tooltip){
7688             this.config[col].tooltip = tooltip;
7689     },
7690
7691     /**
7692      * Returns the dataIndex for the specified column.
7693      * @param {Number} col The column index
7694      * @return {Number}
7695      */
7696     getDataIndex : function(col){
7697         return this.config[col].dataIndex;
7698     },
7699
7700     /**
7701      * Sets the dataIndex for a column.
7702      * @param {Number} col The column index
7703      * @param {Number} dataIndex The new dataIndex
7704      */
7705     setDataIndex : function(col, dataIndex){
7706         this.config[col].dataIndex = dataIndex;
7707     },
7708
7709     
7710     
7711     /**
7712      * Returns true if the cell is editable.
7713      * @param {Number} colIndex The column index
7714      * @param {Number} rowIndex The row index - this is nto actually used..?
7715      * @return {Boolean}
7716      */
7717     isCellEditable : function(colIndex, rowIndex){
7718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7719     },
7720
7721     /**
7722      * Returns the editor defined for the cell/column.
7723      * return false or null to disable editing.
7724      * @param {Number} colIndex The column index
7725      * @param {Number} rowIndex The row index
7726      * @return {Object}
7727      */
7728     getCellEditor : function(colIndex, rowIndex){
7729         return this.config[colIndex].editor;
7730     },
7731
7732     /**
7733      * Sets if a column is editable.
7734      * @param {Number} col The column index
7735      * @param {Boolean} editable True if the column is editable
7736      */
7737     setEditable : function(col, editable){
7738         this.config[col].editable = editable;
7739     },
7740
7741
7742     /**
7743      * Returns true if the column is hidden.
7744      * @param {Number} colIndex The column index
7745      * @return {Boolean}
7746      */
7747     isHidden : function(colIndex){
7748         return this.config[colIndex].hidden;
7749     },
7750
7751
7752     /**
7753      * Returns true if the column width cannot be changed
7754      */
7755     isFixed : function(colIndex){
7756         return this.config[colIndex].fixed;
7757     },
7758
7759     /**
7760      * Returns true if the column can be resized
7761      * @return {Boolean}
7762      */
7763     isResizable : function(colIndex){
7764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7765     },
7766     /**
7767      * Sets if a column is hidden.
7768      * @param {Number} colIndex The column index
7769      * @param {Boolean} hidden True if the column is hidden
7770      */
7771     setHidden : function(colIndex, hidden){
7772         this.config[colIndex].hidden = hidden;
7773         this.totalWidth = null;
7774         this.fireEvent("hiddenchange", this, colIndex, hidden);
7775     },
7776
7777     /**
7778      * Sets the editor for a column.
7779      * @param {Number} col The column index
7780      * @param {Object} editor The editor object
7781      */
7782     setEditor : function(col, editor){
7783         this.config[col].editor = editor;
7784     },
7785     /**
7786      * Add a column (experimental...) - defaults to adding to the end..
7787      * @param {Object} config 
7788     */
7789     addColumn : function(c)
7790     {
7791     
7792         var i = this.config.length;
7793         this.config[i] = c;
7794         
7795         if(typeof c.dataIndex == "undefined"){
7796             c.dataIndex = i;
7797         }
7798         if(typeof c.renderer == "string"){
7799             c.renderer = Roo.util.Format[c.renderer];
7800         }
7801         if(typeof c.id == "undefined"){
7802             c.id = Roo.id();
7803         }
7804         if(c.editor && c.editor.xtype){
7805             c.editor  = Roo.factory(c.editor, Roo.grid);
7806         }
7807         if(c.editor && c.editor.isFormField){
7808             c.editor = new Roo.grid.GridEditor(c.editor);
7809         }
7810         this.lookup[c.id] = c;
7811     }
7812     
7813 });
7814
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7816 {
7817     if(typeof value == "object") {
7818         return value;
7819     }
7820         if(typeof value == "string" && value.length < 1){
7821             return "&#160;";
7822         }
7823     
7824         return String.format("{0}", value);
7825 };
7826
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 /*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839  
7840 /**
7841  * @class Roo.LoadMask
7842  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7843  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7845  * element's UpdateManager load indicator and will be destroyed after the initial load.
7846  * @constructor
7847  * Create a new LoadMask
7848  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849  * @param {Object} config The config object
7850  */
7851 Roo.LoadMask = function(el, config){
7852     this.el = Roo.get(el);
7853     Roo.apply(this, config);
7854     if(this.store){
7855         this.store.on('beforeload', this.onBeforeLoad, this);
7856         this.store.on('load', this.onLoad, this);
7857         this.store.on('loadexception', this.onLoadException, this);
7858         this.removeMask = false;
7859     }else{
7860         var um = this.el.getUpdateManager();
7861         um.showLoadIndicator = false; // disable the default indicator
7862         um.on('beforeupdate', this.onBeforeLoad, this);
7863         um.on('update', this.onLoad, this);
7864         um.on('failure', this.onLoad, this);
7865         this.removeMask = true;
7866     }
7867 };
7868
7869 Roo.LoadMask.prototype = {
7870     /**
7871      * @cfg {Boolean} removeMask
7872      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7874      */
7875     /**
7876      * @cfg {String} msg
7877      * The text to display in a centered loading message box (defaults to 'Loading...')
7878      */
7879     msg : 'Loading...',
7880     /**
7881      * @cfg {String} msgCls
7882      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7883      */
7884     msgCls : 'x-mask-loading',
7885
7886     /**
7887      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7888      * @type Boolean
7889      */
7890     disabled: false,
7891
7892     /**
7893      * Disables the mask to prevent it from being displayed
7894      */
7895     disable : function(){
7896        this.disabled = true;
7897     },
7898
7899     /**
7900      * Enables the mask so that it can be displayed
7901      */
7902     enable : function(){
7903         this.disabled = false;
7904     },
7905     
7906     onLoadException : function()
7907     {
7908         Roo.log(arguments);
7909         
7910         if (typeof(arguments[3]) != 'undefined') {
7911             Roo.MessageBox.alert("Error loading",arguments[3]);
7912         } 
7913         /*
7914         try {
7915             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7917             }   
7918         } catch(e) {
7919             
7920         }
7921         */
7922     
7923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7924     },
7925     // private
7926     onLoad : function()
7927     {
7928         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7929     },
7930
7931     // private
7932     onBeforeLoad : function(){
7933         if(!this.disabled){
7934             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7935         }
7936     },
7937
7938     // private
7939     destroy : function(){
7940         if(this.store){
7941             this.store.un('beforeload', this.onBeforeLoad, this);
7942             this.store.un('load', this.onLoad, this);
7943             this.store.un('loadexception', this.onLoadException, this);
7944         }else{
7945             var um = this.el.getUpdateManager();
7946             um.un('beforeupdate', this.onBeforeLoad, this);
7947             um.un('update', this.onLoad, this);
7948             um.un('failure', this.onLoad, this);
7949         }
7950     }
7951 };/*
7952  * - LGPL
7953  *
7954  * table
7955  * 
7956  */
7957
7958 /**
7959  * @class Roo.bootstrap.Table
7960  * @extends Roo.bootstrap.Component
7961  * Bootstrap Table class
7962  * @cfg {String} cls table class
7963  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964  * @cfg {String} bgcolor Specifies the background color for a table
7965  * @cfg {Number} border Specifies whether the table cells should have borders or not
7966  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967  * @cfg {Number} cellspacing Specifies the space between cells
7968  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970  * @cfg {String} sortable Specifies that the table should be sortable
7971  * @cfg {String} summary Specifies a summary of the content of a table
7972  * @cfg {Number} width Specifies the width of a table
7973  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7974  * 
7975  * @cfg {boolean} striped Should the rows be alternative striped
7976  * @cfg {boolean} bordered Add borders to the table
7977  * @cfg {boolean} hover Add hover highlighting
7978  * @cfg {boolean} condensed Format condensed
7979  * @cfg {boolean} responsive Format condensed
7980  * @cfg {Boolean} loadMask (true|false) default false
7981  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983  * @cfg {Boolean} rowSelection (true|false) default false
7984  * @cfg {Boolean} cellSelection (true|false) default false
7985  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7987  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7988  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7989  
7990  * 
7991  * @constructor
7992  * Create a new Table
7993  * @param {Object} config The config object
7994  */
7995
7996 Roo.bootstrap.Table = function(config){
7997     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7998     
7999   
8000     
8001     // BC...
8002     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8006     
8007     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8008     if (this.sm) {
8009         this.sm.grid = this;
8010         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011         this.sm = this.selModel;
8012         this.sm.xmodule = this.xmodule || false;
8013     }
8014     
8015     if (this.cm && typeof(this.cm.config) == 'undefined') {
8016         this.colModel = new Roo.grid.ColumnModel(this.cm);
8017         this.cm = this.colModel;
8018         this.cm.xmodule = this.xmodule || false;
8019     }
8020     if (this.store) {
8021         this.store= Roo.factory(this.store, Roo.data);
8022         this.ds = this.store;
8023         this.ds.xmodule = this.xmodule || false;
8024          
8025     }
8026     if (this.footer && this.store) {
8027         this.footer.dataSource = this.ds;
8028         this.footer = Roo.factory(this.footer);
8029     }
8030     
8031     /** @private */
8032     this.addEvents({
8033         /**
8034          * @event cellclick
8035          * Fires when a cell is clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Number} columnIndex
8040          * @param {Roo.EventObject} e
8041          */
8042         "cellclick" : true,
8043         /**
8044          * @event celldblclick
8045          * Fires when a cell is double clicked
8046          * @param {Roo.bootstrap.Table} this
8047          * @param {Roo.Element} el
8048          * @param {Number} rowIndex
8049          * @param {Number} columnIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "celldblclick" : true,
8053         /**
8054          * @event rowclick
8055          * Fires when a row is clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Roo.Element} el
8058          * @param {Number} rowIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "rowclick" : true,
8062         /**
8063          * @event rowdblclick
8064          * Fires when a row is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Roo.EventObject} e
8069          */
8070         "rowdblclick" : true,
8071         /**
8072          * @event mouseover
8073          * Fires when a mouseover occur
8074          * @param {Roo.bootstrap.Table} this
8075          * @param {Roo.Element} el
8076          * @param {Number} rowIndex
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "mouseover" : true,
8081         /**
8082          * @event mouseout
8083          * Fires when a mouseout occur
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Number} columnIndex
8088          * @param {Roo.EventObject} e
8089          */
8090         "mouseout" : true,
8091         /**
8092          * @event rowclass
8093          * Fires when a row is rendered, so you can change add a style to it.
8094          * @param {Roo.bootstrap.Table} this
8095          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8096          */
8097         'rowclass' : true,
8098           /**
8099          * @event rowsrendered
8100          * Fires when all the  rows have been rendered
8101          * @param {Roo.bootstrap.Table} this
8102          */
8103         'rowsrendered' : true,
8104         /**
8105          * @event contextmenu
8106          * The raw contextmenu event for the entire grid.
8107          * @param {Roo.EventObject} e
8108          */
8109         "contextmenu" : true,
8110         /**
8111          * @event rowcontextmenu
8112          * Fires when a row is right clicked
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Number} rowIndex
8115          * @param {Roo.EventObject} e
8116          */
8117         "rowcontextmenu" : true,
8118         /**
8119          * @event cellcontextmenu
8120          * Fires when a cell is right clicked
8121          * @param {Roo.bootstrap.Table} this
8122          * @param {Number} rowIndex
8123          * @param {Number} cellIndex
8124          * @param {Roo.EventObject} e
8125          */
8126          "cellcontextmenu" : true,
8127          /**
8128          * @event headercontextmenu
8129          * Fires when a header is right clicked
8130          * @param {Roo.bootstrap.Table} this
8131          * @param {Number} columnIndex
8132          * @param {Roo.EventObject} e
8133          */
8134         "headercontextmenu" : true
8135     });
8136 };
8137
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8139     
8140     cls: false,
8141     align: false,
8142     bgcolor: false,
8143     border: false,
8144     cellpadding: false,
8145     cellspacing: false,
8146     frame: false,
8147     rules: false,
8148     sortable: false,
8149     summary: false,
8150     width: false,
8151     striped : false,
8152     scrollBody : false,
8153     bordered: false,
8154     hover:  false,
8155     condensed : false,
8156     responsive : false,
8157     sm : false,
8158     cm : false,
8159     store : false,
8160     loadMask : false,
8161     footerShow : true,
8162     headerShow : true,
8163   
8164     rowSelection : false,
8165     cellSelection : false,
8166     layout : false,
8167     
8168     // Roo.Element - the tbody
8169     mainBody: false,
8170     // Roo.Element - thead element
8171     mainHead: false,
8172     
8173     container: false, // used by gridpanel...
8174     
8175     lazyLoad : false,
8176     
8177     CSS : Roo.util.CSS,
8178     
8179     auto_hide_footer : false,
8180     
8181     getAutoCreate : function()
8182     {
8183         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8184         
8185         cfg = {
8186             tag: 'table',
8187             cls : 'table',
8188             cn : []
8189         };
8190         if (this.scrollBody) {
8191             cfg.cls += ' table-body-fixed';
8192         }    
8193         if (this.striped) {
8194             cfg.cls += ' table-striped';
8195         }
8196         
8197         if (this.hover) {
8198             cfg.cls += ' table-hover';
8199         }
8200         if (this.bordered) {
8201             cfg.cls += ' table-bordered';
8202         }
8203         if (this.condensed) {
8204             cfg.cls += ' table-condensed';
8205         }
8206         if (this.responsive) {
8207             cfg.cls += ' table-responsive';
8208         }
8209         
8210         if (this.cls) {
8211             cfg.cls+=  ' ' +this.cls;
8212         }
8213         
8214         // this lot should be simplifed...
8215         var _t = this;
8216         var cp = [
8217             'align',
8218             'bgcolor',
8219             'border',
8220             'cellpadding',
8221             'cellspacing',
8222             'frame',
8223             'rules',
8224             'sortable',
8225             'summary',
8226             'width'
8227         ].forEach(function(k) {
8228             if (_t[k]) {
8229                 cfg[k] = _t[k];
8230             }
8231         });
8232         
8233         
8234         if (this.layout) {
8235             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236         }
8237         
8238         if(this.store || this.cm){
8239             if(this.headerShow){
8240                 cfg.cn.push(this.renderHeader());
8241             }
8242             
8243             cfg.cn.push(this.renderBody());
8244             
8245             if(this.footerShow){
8246                 cfg.cn.push(this.renderFooter());
8247             }
8248             // where does this come from?
8249             //cfg.cls+=  ' TableGrid';
8250         }
8251         
8252         return { cn : [ cfg ] };
8253     },
8254     
8255     initEvents : function()
8256     {   
8257         if(!this.store || !this.cm){
8258             return;
8259         }
8260         if (this.selModel) {
8261             this.selModel.initEvents();
8262         }
8263         
8264         
8265         //Roo.log('initEvents with ds!!!!');
8266         
8267         this.mainBody = this.el.select('tbody', true).first();
8268         this.mainHead = this.el.select('thead', true).first();
8269         this.mainFoot = this.el.select('tfoot', true).first();
8270         
8271         
8272         
8273         
8274         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275             e.on('click', this.sort, this);
8276         }, this);
8277         
8278         this.mainBody.on("click", this.onClick, this);
8279         this.mainBody.on("dblclick", this.onDblClick, this);
8280         
8281         // why is this done????? = it breaks dialogs??
8282         //this.parent().el.setStyle('position', 'relative');
8283         
8284         
8285         if (this.footer) {
8286             this.footer.parentId = this.id;
8287             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8288             
8289             if(this.lazyLoad){
8290                 this.el.select('tfoot tr td').first().addClass('hide');
8291             }
8292         } 
8293         
8294         if(this.loadMask) {
8295             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8296         }
8297         
8298         this.store.on('load', this.onLoad, this);
8299         this.store.on('beforeload', this.onBeforeLoad, this);
8300         this.store.on('update', this.onUpdate, this);
8301         this.store.on('add', this.onAdd, this);
8302         this.store.on("clear", this.clear, this);
8303         
8304         this.el.on("contextmenu", this.onContextMenu, this);
8305         
8306         this.mainBody.on('scroll', this.onBodyScroll, this);
8307         
8308         this.cm.on("headerchange", this.onHeaderChange, this);
8309         
8310         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8311         
8312     },
8313     
8314     onContextMenu : function(e, t)
8315     {
8316         this.processEvent("contextmenu", e);
8317     },
8318     
8319     processEvent : function(name, e)
8320     {
8321         if (name != 'touchstart' ) {
8322             this.fireEvent(name, e);    
8323         }
8324         
8325         var t = e.getTarget();
8326         
8327         var cell = Roo.get(t);
8328         
8329         if(!cell){
8330             return;
8331         }
8332         
8333         if(cell.findParent('tfoot', false, true)){
8334             return;
8335         }
8336         
8337         if(cell.findParent('thead', false, true)){
8338             
8339             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340                 cell = Roo.get(t).findParent('th', false, true);
8341                 if (!cell) {
8342                     Roo.log("failed to find th in thead?");
8343                     Roo.log(e.getTarget());
8344                     return;
8345                 }
8346             }
8347             
8348             var cellIndex = cell.dom.cellIndex;
8349             
8350             var ename = name == 'touchstart' ? 'click' : name;
8351             this.fireEvent("header" + ename, this, cellIndex, e);
8352             
8353             return;
8354         }
8355         
8356         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357             cell = Roo.get(t).findParent('td', false, true);
8358             if (!cell) {
8359                 Roo.log("failed to find th in tbody?");
8360                 Roo.log(e.getTarget());
8361                 return;
8362             }
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1;
8368         
8369         if(row !== false){
8370             
8371             this.fireEvent("row" + name, this, rowIndex, e);
8372             
8373             if(cell !== false){
8374             
8375                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8376             }
8377         }
8378         
8379     },
8380     
8381     onMouseover : function(e, el)
8382     {
8383         var cell = Roo.get(el);
8384         
8385         if(!cell){
8386             return;
8387         }
8388         
8389         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390             cell = cell.findParent('td', false, true);
8391         }
8392         
8393         var row = cell.findParent('tr', false, true);
8394         var cellIndex = cell.dom.cellIndex;
8395         var rowIndex = row.dom.rowIndex - 1; // start from 0
8396         
8397         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8398         
8399     },
8400     
8401     onMouseout : function(e, el)
8402     {
8403         var cell = Roo.get(el);
8404         
8405         if(!cell){
8406             return;
8407         }
8408         
8409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410             cell = cell.findParent('td', false, true);
8411         }
8412         
8413         var row = cell.findParent('tr', false, true);
8414         var cellIndex = cell.dom.cellIndex;
8415         var rowIndex = row.dom.rowIndex - 1; // start from 0
8416         
8417         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8418         
8419     },
8420     
8421     onClick : function(e, el)
8422     {
8423         var cell = Roo.get(el);
8424         
8425         if(!cell || (!this.cellSelection && !this.rowSelection)){
8426             return;
8427         }
8428         
8429         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430             cell = cell.findParent('td', false, true);
8431         }
8432         
8433         if(!cell || typeof(cell) == 'undefined'){
8434             return;
8435         }
8436         
8437         var row = cell.findParent('tr', false, true);
8438         
8439         if(!row || typeof(row) == 'undefined'){
8440             return;
8441         }
8442         
8443         var cellIndex = cell.dom.cellIndex;
8444         var rowIndex = this.getRowIndex(row);
8445         
8446         // why??? - should these not be based on SelectionModel?
8447         //if(this.cellSelection){
8448             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8449         //}
8450         
8451         //if(this.rowSelection){
8452             this.fireEvent('rowclick', this, row, rowIndex, e);
8453         //}
8454          
8455     },
8456         
8457     onDblClick : function(e,el)
8458     {
8459         var cell = Roo.get(el);
8460         
8461         if(!cell || (!this.cellSelection && !this.rowSelection)){
8462             return;
8463         }
8464         
8465         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8466             cell = cell.findParent('td', false, true);
8467         }
8468         
8469         if(!cell || typeof(cell) == 'undefined'){
8470             return;
8471         }
8472         
8473         var row = cell.findParent('tr', false, true);
8474         
8475         if(!row || typeof(row) == 'undefined'){
8476             return;
8477         }
8478         
8479         var cellIndex = cell.dom.cellIndex;
8480         var rowIndex = this.getRowIndex(row);
8481         
8482         if(this.cellSelection){
8483             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8484         }
8485         
8486         if(this.rowSelection){
8487             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8488         }
8489     },
8490     
8491     sort : function(e,el)
8492     {
8493         var col = Roo.get(el);
8494         
8495         if(!col.hasClass('sortable')){
8496             return;
8497         }
8498         
8499         var sort = col.attr('sort');
8500         var dir = 'ASC';
8501         
8502         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8503             dir = 'DESC';
8504         }
8505         
8506         this.store.sortInfo = {field : sort, direction : dir};
8507         
8508         if (this.footer) {
8509             Roo.log("calling footer first");
8510             this.footer.onClick('first');
8511         } else {
8512         
8513             this.store.load({ params : { start : 0 } });
8514         }
8515     },
8516     
8517     renderHeader : function()
8518     {
8519         var header = {
8520             tag: 'thead',
8521             cn : []
8522         };
8523         
8524         var cm = this.cm;
8525         this.totalWidth = 0;
8526         
8527         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8528             
8529             var config = cm.config[i];
8530             
8531             var c = {
8532                 tag: 'th',
8533                 cls : 'x-hcol-' + i,
8534                 style : '',
8535                 
8536                 html: cm.getColumnHeader(i)
8537             };
8538             
8539             var tooltip = cm.getColumnTooltip(i);
8540             if (tooltip) {
8541                 c.tooltip = tooltip;
8542             }
8543             
8544             
8545             var hh = '';
8546             
8547             if(typeof(config.sortable) != 'undefined' && config.sortable){
8548                 c.cls = 'sortable';
8549                 c.html = '<i class="fa"></i>' + c.html;
8550             }
8551             
8552             // could use BS4 hidden-..-down 
8553             
8554             if(typeof(config.lgHeader) != 'undefined'){
8555                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8556             }
8557             
8558             if(typeof(config.mdHeader) != 'undefined'){
8559                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8560             }
8561             
8562             if(typeof(config.smHeader) != 'undefined'){
8563                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8564             }
8565             
8566             if(typeof(config.xsHeader) != 'undefined'){
8567                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8568             }
8569             
8570             if(hh.length){
8571                 c.html = hh;
8572             }
8573             
8574             if(typeof(config.tooltip) != 'undefined'){
8575                 c.tooltip = config.tooltip;
8576             }
8577             
8578             if(typeof(config.colspan) != 'undefined'){
8579                 c.colspan = config.colspan;
8580             }
8581             
8582             if(typeof(config.hidden) != 'undefined' && config.hidden){
8583                 c.style += ' display:none;';
8584             }
8585             
8586             if(typeof(config.dataIndex) != 'undefined'){
8587                 c.sort = config.dataIndex;
8588             }
8589             
8590            
8591             
8592             if(typeof(config.align) != 'undefined' && config.align.length){
8593                 c.style += ' text-align:' + config.align + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 c.style += ' width:' + config.width + 'px;';
8598                 this.totalWidth += config.width;
8599             } else {
8600                 this.totalWidth += 100; // assume minimum of 100 per column?
8601             }
8602             
8603             if(typeof(config.cls) != 'undefined'){
8604                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8605             }
8606             
8607             ['xs','sm','md','lg'].map(function(size){
8608                 
8609                 if(typeof(config[size]) == 'undefined'){
8610                     return;
8611                 }
8612                  
8613                 if (!config[size]) { // 0 = hidden
8614                     // BS 4 '0' is treated as hide that column and below.
8615                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8616                     return;
8617                 }
8618                 
8619                 c.cls += ' col-' + size + '-' + config[size] + (
8620                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8621                 );
8622                 
8623                 
8624             });
8625             
8626             header.cn.push(c)
8627         }
8628         
8629         return header;
8630     },
8631     
8632     renderBody : function()
8633     {
8634         var body = {
8635             tag: 'tbody',
8636             cn : [
8637                 {
8638                     tag: 'tr',
8639                     cn : [
8640                         {
8641                             tag : 'td',
8642                             colspan :  this.cm.getColumnCount()
8643                         }
8644                     ]
8645                 }
8646             ]
8647         };
8648         
8649         return body;
8650     },
8651     
8652     renderFooter : function()
8653     {
8654         var footer = {
8655             tag: 'tfoot',
8656             cn : [
8657                 {
8658                     tag: 'tr',
8659                     cn : [
8660                         {
8661                             tag : 'td',
8662                             colspan :  this.cm.getColumnCount()
8663                         }
8664                     ]
8665                 }
8666             ]
8667         };
8668         
8669         return footer;
8670     },
8671     
8672     
8673     
8674     onLoad : function()
8675     {
8676 //        Roo.log('ds onload');
8677         this.clear();
8678         
8679         var _this = this;
8680         var cm = this.cm;
8681         var ds = this.store;
8682         
8683         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8684             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8685             if (_this.store.sortInfo) {
8686                     
8687                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8688                     e.select('i', true).addClass(['fa-arrow-up']);
8689                 }
8690                 
8691                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8692                     e.select('i', true).addClass(['fa-arrow-down']);
8693                 }
8694             }
8695         });
8696         
8697         var tbody =  this.mainBody;
8698               
8699         if(ds.getCount() > 0){
8700             ds.data.each(function(d,rowIndex){
8701                 var row =  this.renderRow(cm, ds, rowIndex);
8702                 
8703                 tbody.createChild(row);
8704                 
8705                 var _this = this;
8706                 
8707                 if(row.cellObjects.length){
8708                     Roo.each(row.cellObjects, function(r){
8709                         _this.renderCellObject(r);
8710                     })
8711                 }
8712                 
8713             }, this);
8714         }
8715         
8716         var tfoot = this.el.select('tfoot', true).first();
8717         
8718         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8719             
8720             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8721             
8722             var total = this.ds.getTotalCount();
8723             
8724             if(this.footer.pageSize < total){
8725                 this.mainFoot.show();
8726             }
8727         }
8728         
8729         Roo.each(this.el.select('tbody td', true).elements, function(e){
8730             e.on('mouseover', _this.onMouseover, _this);
8731         });
8732         
8733         Roo.each(this.el.select('tbody td', true).elements, function(e){
8734             e.on('mouseout', _this.onMouseout, _this);
8735         });
8736         this.fireEvent('rowsrendered', this);
8737         
8738         this.autoSize();
8739     },
8740     
8741     
8742     onUpdate : function(ds,record)
8743     {
8744         this.refreshRow(record);
8745         this.autoSize();
8746     },
8747     
8748     onRemove : function(ds, record, index, isUpdate){
8749         if(isUpdate !== true){
8750             this.fireEvent("beforerowremoved", this, index, record);
8751         }
8752         var bt = this.mainBody.dom;
8753         
8754         var rows = this.el.select('tbody > tr', true).elements;
8755         
8756         if(typeof(rows[index]) != 'undefined'){
8757             bt.removeChild(rows[index].dom);
8758         }
8759         
8760 //        if(bt.rows[index]){
8761 //            bt.removeChild(bt.rows[index]);
8762 //        }
8763         
8764         if(isUpdate !== true){
8765             //this.stripeRows(index);
8766             //this.syncRowHeights(index, index);
8767             //this.layout();
8768             this.fireEvent("rowremoved", this, index, record);
8769         }
8770     },
8771     
8772     onAdd : function(ds, records, rowIndex)
8773     {
8774         //Roo.log('on Add called');
8775         // - note this does not handle multiple adding very well..
8776         var bt = this.mainBody.dom;
8777         for (var i =0 ; i < records.length;i++) {
8778             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8779             //Roo.log(records[i]);
8780             //Roo.log(this.store.getAt(rowIndex+i));
8781             this.insertRow(this.store, rowIndex + i, false);
8782             return;
8783         }
8784         
8785     },
8786     
8787     
8788     refreshRow : function(record){
8789         var ds = this.store, index;
8790         if(typeof record == 'number'){
8791             index = record;
8792             record = ds.getAt(index);
8793         }else{
8794             index = ds.indexOf(record);
8795             if (index < 0) {
8796                 return; // should not happen - but seems to 
8797             }
8798         }
8799         this.insertRow(ds, index, true);
8800         this.autoSize();
8801         this.onRemove(ds, record, index+1, true);
8802         this.autoSize();
8803         //this.syncRowHeights(index, index);
8804         //this.layout();
8805         this.fireEvent("rowupdated", this, index, record);
8806     },
8807     
8808     insertRow : function(dm, rowIndex, isUpdate){
8809         
8810         if(!isUpdate){
8811             this.fireEvent("beforerowsinserted", this, rowIndex);
8812         }
8813             //var s = this.getScrollState();
8814         var row = this.renderRow(this.cm, this.store, rowIndex);
8815         // insert before rowIndex..
8816         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8817         
8818         var _this = this;
8819                 
8820         if(row.cellObjects.length){
8821             Roo.each(row.cellObjects, function(r){
8822                 _this.renderCellObject(r);
8823             })
8824         }
8825             
8826         if(!isUpdate){
8827             this.fireEvent("rowsinserted", this, rowIndex);
8828             //this.syncRowHeights(firstRow, lastRow);
8829             //this.stripeRows(firstRow);
8830             //this.layout();
8831         }
8832         
8833     },
8834     
8835     
8836     getRowDom : function(rowIndex)
8837     {
8838         var rows = this.el.select('tbody > tr', true).elements;
8839         
8840         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8841         
8842     },
8843     // returns the object tree for a tr..
8844   
8845     
8846     renderRow : function(cm, ds, rowIndex) 
8847     {
8848         var d = ds.getAt(rowIndex);
8849         
8850         var row = {
8851             tag : 'tr',
8852             cls : 'x-row-' + rowIndex,
8853             cn : []
8854         };
8855             
8856         var cellObjects = [];
8857         
8858         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8859             var config = cm.config[i];
8860             
8861             var renderer = cm.getRenderer(i);
8862             var value = '';
8863             var id = false;
8864             
8865             if(typeof(renderer) !== 'undefined'){
8866                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8867             }
8868             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8869             // and are rendered into the cells after the row is rendered - using the id for the element.
8870             
8871             if(typeof(value) === 'object'){
8872                 id = Roo.id();
8873                 cellObjects.push({
8874                     container : id,
8875                     cfg : value 
8876                 })
8877             }
8878             
8879             var rowcfg = {
8880                 record: d,
8881                 rowIndex : rowIndex,
8882                 colIndex : i,
8883                 rowClass : ''
8884             };
8885
8886             this.fireEvent('rowclass', this, rowcfg);
8887             
8888             var td = {
8889                 tag: 'td',
8890                 // this might end up displaying HTML?
8891                 // this is too messy... - better to only do it on columsn you know are going to be too long
8892                 //tooltip : (typeof(value) === 'object') ? '' : value,
8893                 cls : rowcfg.rowClass + ' x-col-' + i,
8894                 style: '',
8895                 html: (typeof(value) === 'object') ? '' : value
8896             };
8897             
8898             if (id) {
8899                 td.id = id;
8900             }
8901             
8902             if(typeof(config.colspan) != 'undefined'){
8903                 td.colspan = config.colspan;
8904             }
8905             
8906             if(typeof(config.hidden) != 'undefined' && config.hidden){
8907                 td.style += ' display:none;';
8908             }
8909             
8910             if(typeof(config.align) != 'undefined' && config.align.length){
8911                 td.style += ' text-align:' + config.align + ';';
8912             }
8913             if(typeof(config.valign) != 'undefined' && config.valign.length){
8914                 td.style += ' vertical-align:' + config.valign + ';';
8915             }
8916             
8917             if(typeof(config.width) != 'undefined'){
8918                 td.style += ' width:' +  config.width + 'px;';
8919             }
8920             
8921             if(typeof(config.cursor) != 'undefined'){
8922                 td.style += ' cursor:' +  config.cursor + ';';
8923             }
8924             
8925             if(typeof(config.cls) != 'undefined'){
8926                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8927             }
8928             
8929             ['xs','sm','md','lg'].map(function(size){
8930                 
8931                 if(typeof(config[size]) == 'undefined'){
8932                     return;
8933                 }
8934                 
8935                 
8936                   
8937                 if (!config[size]) { // 0 = hidden
8938                     // BS 4 '0' is treated as hide that column and below.
8939                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8940                     return;
8941                 }
8942                 
8943                 td.cls += ' col-' + size + '-' + config[size] + (
8944                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8945                 );
8946                  
8947
8948             });
8949             
8950             row.cn.push(td);
8951            
8952         }
8953         
8954         row.cellObjects = cellObjects;
8955         
8956         return row;
8957           
8958     },
8959     
8960     
8961     
8962     onBeforeLoad : function()
8963     {
8964         
8965     },
8966      /**
8967      * Remove all rows
8968      */
8969     clear : function()
8970     {
8971         this.el.select('tbody', true).first().dom.innerHTML = '';
8972     },
8973     /**
8974      * Show or hide a row.
8975      * @param {Number} rowIndex to show or hide
8976      * @param {Boolean} state hide
8977      */
8978     setRowVisibility : function(rowIndex, state)
8979     {
8980         var bt = this.mainBody.dom;
8981         
8982         var rows = this.el.select('tbody > tr', true).elements;
8983         
8984         if(typeof(rows[rowIndex]) == 'undefined'){
8985             return;
8986         }
8987         rows[rowIndex].dom.style.display = state ? '' : 'none';
8988     },
8989     
8990     
8991     getSelectionModel : function(){
8992         if(!this.selModel){
8993             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8994         }
8995         return this.selModel;
8996     },
8997     /*
8998      * Render the Roo.bootstrap object from renderder
8999      */
9000     renderCellObject : function(r)
9001     {
9002         var _this = this;
9003         
9004         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9005         
9006         var t = r.cfg.render(r.container);
9007         
9008         if(r.cfg.cn){
9009             Roo.each(r.cfg.cn, function(c){
9010                 var child = {
9011                     container: t.getChildContainer(),
9012                     cfg: c
9013                 };
9014                 _this.renderCellObject(child);
9015             })
9016         }
9017     },
9018     
9019     getRowIndex : function(row)
9020     {
9021         var rowIndex = -1;
9022         
9023         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9024             if(el != row){
9025                 return;
9026             }
9027             
9028             rowIndex = index;
9029         });
9030         
9031         return rowIndex;
9032     },
9033      /**
9034      * Returns the grid's underlying element = used by panel.Grid
9035      * @return {Element} The element
9036      */
9037     getGridEl : function(){
9038         return this.el;
9039     },
9040      /**
9041      * Forces a resize - used by panel.Grid
9042      * @return {Element} The element
9043      */
9044     autoSize : function()
9045     {
9046         //var ctr = Roo.get(this.container.dom.parentElement);
9047         var ctr = Roo.get(this.el.dom);
9048         
9049         var thd = this.getGridEl().select('thead',true).first();
9050         var tbd = this.getGridEl().select('tbody', true).first();
9051         var tfd = this.getGridEl().select('tfoot', true).first();
9052         
9053         var cw = ctr.getWidth();
9054         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9055         
9056         if (tbd) {
9057             
9058             tbd.setWidth(ctr.getWidth());
9059             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9060             // this needs fixing for various usage - currently only hydra job advers I think..
9061             //tdb.setHeight(
9062             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9063             //); 
9064             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9065             cw -= barsize;
9066         }
9067         cw = Math.max(cw, this.totalWidth);
9068         this.getGridEl().select('tbody tr',true).setWidth(cw);
9069         
9070         // resize 'expandable coloumn?
9071         
9072         return; // we doe not have a view in this design..
9073         
9074     },
9075     onBodyScroll: function()
9076     {
9077         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9078         if(this.mainHead){
9079             this.mainHead.setStyle({
9080                 'position' : 'relative',
9081                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9082             });
9083         }
9084         
9085         if(this.lazyLoad){
9086             
9087             var scrollHeight = this.mainBody.dom.scrollHeight;
9088             
9089             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9090             
9091             var height = this.mainBody.getHeight();
9092             
9093             if(scrollHeight - height == scrollTop) {
9094                 
9095                 var total = this.ds.getTotalCount();
9096                 
9097                 if(this.footer.cursor + this.footer.pageSize < total){
9098                     
9099                     this.footer.ds.load({
9100                         params : {
9101                             start : this.footer.cursor + this.footer.pageSize,
9102                             limit : this.footer.pageSize
9103                         },
9104                         add : true
9105                     });
9106                 }
9107             }
9108             
9109         }
9110     },
9111     
9112     onHeaderChange : function()
9113     {
9114         var header = this.renderHeader();
9115         var table = this.el.select('table', true).first();
9116         
9117         this.mainHead.remove();
9118         this.mainHead = table.createChild(header, this.mainBody, false);
9119         
9120         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9121             e.on('click', this.sort, this);
9122         }, this);
9123         
9124         
9125     },
9126     
9127     onHiddenChange : function(colModel, colIndex, hidden)
9128     {
9129         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9130         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9131         
9132         this.CSS.updateRule(thSelector, "display", "");
9133         this.CSS.updateRule(tdSelector, "display", "");
9134         
9135         if(hidden){
9136             this.CSS.updateRule(thSelector, "display", "none");
9137             this.CSS.updateRule(tdSelector, "display", "none");
9138         }
9139         
9140         this.onHeaderChange();
9141         this.onLoad();
9142     },
9143     
9144     setColumnWidth: function(col_index, width)
9145     {
9146         // width = "md-2 xs-2..."
9147         if(!this.colModel.config[col_index]) {
9148             return;
9149         }
9150         
9151         var w = width.split(" ");
9152         
9153         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9154         
9155         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9156         
9157         
9158         for(var j = 0; j < w.length; j++) {
9159             
9160             if(!w[j]) {
9161                 continue;
9162             }
9163             
9164             var size_cls = w[j].split("-");
9165             
9166             if(!Number.isInteger(size_cls[1] * 1)) {
9167                 continue;
9168             }
9169             
9170             if(!this.colModel.config[col_index][size_cls[0]]) {
9171                 continue;
9172             }
9173             
9174             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9175                 continue;
9176             }
9177             
9178             h_row[0].classList.replace(
9179                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9180                 "col-"+size_cls[0]+"-"+size_cls[1]
9181             );
9182             
9183             for(var i = 0; i < rows.length; i++) {
9184                 
9185                 var size_cls = w[j].split("-");
9186                 
9187                 if(!Number.isInteger(size_cls[1] * 1)) {
9188                     continue;
9189                 }
9190                 
9191                 if(!this.colModel.config[col_index][size_cls[0]]) {
9192                     continue;
9193                 }
9194                 
9195                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9196                     continue;
9197                 }
9198                 
9199                 rows[i].classList.replace(
9200                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9201                     "col-"+size_cls[0]+"-"+size_cls[1]
9202                 );
9203             }
9204             
9205             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9206         }
9207     }
9208 });
9209
9210  
9211
9212  /*
9213  * - LGPL
9214  *
9215  * table cell
9216  * 
9217  */
9218
9219 /**
9220  * @class Roo.bootstrap.TableCell
9221  * @extends Roo.bootstrap.Component
9222  * Bootstrap TableCell class
9223  * @cfg {String} html cell contain text
9224  * @cfg {String} cls cell class
9225  * @cfg {String} tag cell tag (td|th) default td
9226  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9227  * @cfg {String} align Aligns the content in a cell
9228  * @cfg {String} axis Categorizes cells
9229  * @cfg {String} bgcolor Specifies the background color of a cell
9230  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9231  * @cfg {Number} colspan Specifies the number of columns a cell should span
9232  * @cfg {String} headers Specifies one or more header cells a cell is related to
9233  * @cfg {Number} height Sets the height of a cell
9234  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9235  * @cfg {Number} rowspan Sets the number of rows a cell should span
9236  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9237  * @cfg {String} valign Vertical aligns the content in a cell
9238  * @cfg {Number} width Specifies the width of a cell
9239  * 
9240  * @constructor
9241  * Create a new TableCell
9242  * @param {Object} config The config object
9243  */
9244
9245 Roo.bootstrap.TableCell = function(config){
9246     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9247 };
9248
9249 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9250     
9251     html: false,
9252     cls: false,
9253     tag: false,
9254     abbr: false,
9255     align: false,
9256     axis: false,
9257     bgcolor: false,
9258     charoff: false,
9259     colspan: false,
9260     headers: false,
9261     height: false,
9262     nowrap: false,
9263     rowspan: false,
9264     scope: false,
9265     valign: false,
9266     width: false,
9267     
9268     
9269     getAutoCreate : function(){
9270         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9271         
9272         cfg = {
9273             tag: 'td'
9274         };
9275         
9276         if(this.tag){
9277             cfg.tag = this.tag;
9278         }
9279         
9280         if (this.html) {
9281             cfg.html=this.html
9282         }
9283         if (this.cls) {
9284             cfg.cls=this.cls
9285         }
9286         if (this.abbr) {
9287             cfg.abbr=this.abbr
9288         }
9289         if (this.align) {
9290             cfg.align=this.align
9291         }
9292         if (this.axis) {
9293             cfg.axis=this.axis
9294         }
9295         if (this.bgcolor) {
9296             cfg.bgcolor=this.bgcolor
9297         }
9298         if (this.charoff) {
9299             cfg.charoff=this.charoff
9300         }
9301         if (this.colspan) {
9302             cfg.colspan=this.colspan
9303         }
9304         if (this.headers) {
9305             cfg.headers=this.headers
9306         }
9307         if (this.height) {
9308             cfg.height=this.height
9309         }
9310         if (this.nowrap) {
9311             cfg.nowrap=this.nowrap
9312         }
9313         if (this.rowspan) {
9314             cfg.rowspan=this.rowspan
9315         }
9316         if (this.scope) {
9317             cfg.scope=this.scope
9318         }
9319         if (this.valign) {
9320             cfg.valign=this.valign
9321         }
9322         if (this.width) {
9323             cfg.width=this.width
9324         }
9325         
9326         
9327         return cfg;
9328     }
9329    
9330 });
9331
9332  
9333
9334  /*
9335  * - LGPL
9336  *
9337  * table row
9338  * 
9339  */
9340
9341 /**
9342  * @class Roo.bootstrap.TableRow
9343  * @extends Roo.bootstrap.Component
9344  * Bootstrap TableRow class
9345  * @cfg {String} cls row class
9346  * @cfg {String} align Aligns the content in a table row
9347  * @cfg {String} bgcolor Specifies a background color for a table row
9348  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9349  * @cfg {String} valign Vertical aligns the content in a table row
9350  * 
9351  * @constructor
9352  * Create a new TableRow
9353  * @param {Object} config The config object
9354  */
9355
9356 Roo.bootstrap.TableRow = function(config){
9357     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9358 };
9359
9360 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9361     
9362     cls: false,
9363     align: false,
9364     bgcolor: false,
9365     charoff: false,
9366     valign: false,
9367     
9368     getAutoCreate : function(){
9369         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9370         
9371         cfg = {
9372             tag: 'tr'
9373         };
9374             
9375         if(this.cls){
9376             cfg.cls = this.cls;
9377         }
9378         if(this.align){
9379             cfg.align = this.align;
9380         }
9381         if(this.bgcolor){
9382             cfg.bgcolor = this.bgcolor;
9383         }
9384         if(this.charoff){
9385             cfg.charoff = this.charoff;
9386         }
9387         if(this.valign){
9388             cfg.valign = this.valign;
9389         }
9390         
9391         return cfg;
9392     }
9393    
9394 });
9395
9396  
9397
9398  /*
9399  * - LGPL
9400  *
9401  * table body
9402  * 
9403  */
9404
9405 /**
9406  * @class Roo.bootstrap.TableBody
9407  * @extends Roo.bootstrap.Component
9408  * Bootstrap TableBody class
9409  * @cfg {String} cls element class
9410  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9411  * @cfg {String} align Aligns the content inside the element
9412  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9413  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9414  * 
9415  * @constructor
9416  * Create a new TableBody
9417  * @param {Object} config The config object
9418  */
9419
9420 Roo.bootstrap.TableBody = function(config){
9421     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9422 };
9423
9424 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9425     
9426     cls: false,
9427     tag: false,
9428     align: false,
9429     charoff: false,
9430     valign: false,
9431     
9432     getAutoCreate : function(){
9433         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9434         
9435         cfg = {
9436             tag: 'tbody'
9437         };
9438             
9439         if (this.cls) {
9440             cfg.cls=this.cls
9441         }
9442         if(this.tag){
9443             cfg.tag = this.tag;
9444         }
9445         
9446         if(this.align){
9447             cfg.align = this.align;
9448         }
9449         if(this.charoff){
9450             cfg.charoff = this.charoff;
9451         }
9452         if(this.valign){
9453             cfg.valign = this.valign;
9454         }
9455         
9456         return cfg;
9457     }
9458     
9459     
9460 //    initEvents : function()
9461 //    {
9462 //        
9463 //        if(!this.store){
9464 //            return;
9465 //        }
9466 //        
9467 //        this.store = Roo.factory(this.store, Roo.data);
9468 //        this.store.on('load', this.onLoad, this);
9469 //        
9470 //        this.store.load();
9471 //        
9472 //    },
9473 //    
9474 //    onLoad: function () 
9475 //    {   
9476 //        this.fireEvent('load', this);
9477 //    }
9478 //    
9479 //   
9480 });
9481
9482  
9483
9484  /*
9485  * Based on:
9486  * Ext JS Library 1.1.1
9487  * Copyright(c) 2006-2007, Ext JS, LLC.
9488  *
9489  * Originally Released Under LGPL - original licence link has changed is not relivant.
9490  *
9491  * Fork - LGPL
9492  * <script type="text/javascript">
9493  */
9494
9495 // as we use this in bootstrap.
9496 Roo.namespace('Roo.form');
9497  /**
9498  * @class Roo.form.Action
9499  * Internal Class used to handle form actions
9500  * @constructor
9501  * @param {Roo.form.BasicForm} el The form element or its id
9502  * @param {Object} config Configuration options
9503  */
9504
9505  
9506  
9507 // define the action interface
9508 Roo.form.Action = function(form, options){
9509     this.form = form;
9510     this.options = options || {};
9511 };
9512 /**
9513  * Client Validation Failed
9514  * @const 
9515  */
9516 Roo.form.Action.CLIENT_INVALID = 'client';
9517 /**
9518  * Server Validation Failed
9519  * @const 
9520  */
9521 Roo.form.Action.SERVER_INVALID = 'server';
9522  /**
9523  * Connect to Server Failed
9524  * @const 
9525  */
9526 Roo.form.Action.CONNECT_FAILURE = 'connect';
9527 /**
9528  * Reading Data from Server Failed
9529  * @const 
9530  */
9531 Roo.form.Action.LOAD_FAILURE = 'load';
9532
9533 Roo.form.Action.prototype = {
9534     type : 'default',
9535     failureType : undefined,
9536     response : undefined,
9537     result : undefined,
9538
9539     // interface method
9540     run : function(options){
9541
9542     },
9543
9544     // interface method
9545     success : function(response){
9546
9547     },
9548
9549     // interface method
9550     handleResponse : function(response){
9551
9552     },
9553
9554     // default connection failure
9555     failure : function(response){
9556         
9557         this.response = response;
9558         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9559         this.form.afterAction(this, false);
9560     },
9561
9562     processResponse : function(response){
9563         this.response = response;
9564         if(!response.responseText){
9565             return true;
9566         }
9567         this.result = this.handleResponse(response);
9568         return this.result;
9569     },
9570
9571     // utility functions used internally
9572     getUrl : function(appendParams){
9573         var url = this.options.url || this.form.url || this.form.el.dom.action;
9574         if(appendParams){
9575             var p = this.getParams();
9576             if(p){
9577                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9578             }
9579         }
9580         return url;
9581     },
9582
9583     getMethod : function(){
9584         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9585     },
9586
9587     getParams : function(){
9588         var bp = this.form.baseParams;
9589         var p = this.options.params;
9590         if(p){
9591             if(typeof p == "object"){
9592                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9593             }else if(typeof p == 'string' && bp){
9594                 p += '&' + Roo.urlEncode(bp);
9595             }
9596         }else if(bp){
9597             p = Roo.urlEncode(bp);
9598         }
9599         return p;
9600     },
9601
9602     createCallback : function(){
9603         return {
9604             success: this.success,
9605             failure: this.failure,
9606             scope: this,
9607             timeout: (this.form.timeout*1000),
9608             upload: this.form.fileUpload ? this.success : undefined
9609         };
9610     }
9611 };
9612
9613 Roo.form.Action.Submit = function(form, options){
9614     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9615 };
9616
9617 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9618     type : 'submit',
9619
9620     haveProgress : false,
9621     uploadComplete : false,
9622     
9623     // uploadProgress indicator.
9624     uploadProgress : function()
9625     {
9626         if (!this.form.progressUrl) {
9627             return;
9628         }
9629         
9630         if (!this.haveProgress) {
9631             Roo.MessageBox.progress("Uploading", "Uploading");
9632         }
9633         if (this.uploadComplete) {
9634            Roo.MessageBox.hide();
9635            return;
9636         }
9637         
9638         this.haveProgress = true;
9639    
9640         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9641         
9642         var c = new Roo.data.Connection();
9643         c.request({
9644             url : this.form.progressUrl,
9645             params: {
9646                 id : uid
9647             },
9648             method: 'GET',
9649             success : function(req){
9650                //console.log(data);
9651                 var rdata = false;
9652                 var edata;
9653                 try  {
9654                    rdata = Roo.decode(req.responseText)
9655                 } catch (e) {
9656                     Roo.log("Invalid data from server..");
9657                     Roo.log(edata);
9658                     return;
9659                 }
9660                 if (!rdata || !rdata.success) {
9661                     Roo.log(rdata);
9662                     Roo.MessageBox.alert(Roo.encode(rdata));
9663                     return;
9664                 }
9665                 var data = rdata.data;
9666                 
9667                 if (this.uploadComplete) {
9668                    Roo.MessageBox.hide();
9669                    return;
9670                 }
9671                    
9672                 if (data){
9673                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9674                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9675                     );
9676                 }
9677                 this.uploadProgress.defer(2000,this);
9678             },
9679        
9680             failure: function(data) {
9681                 Roo.log('progress url failed ');
9682                 Roo.log(data);
9683             },
9684             scope : this
9685         });
9686            
9687     },
9688     
9689     
9690     run : function()
9691     {
9692         // run get Values on the form, so it syncs any secondary forms.
9693         this.form.getValues();
9694         
9695         var o = this.options;
9696         var method = this.getMethod();
9697         var isPost = method == 'POST';
9698         if(o.clientValidation === false || this.form.isValid()){
9699             
9700             if (this.form.progressUrl) {
9701                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9702                     (new Date() * 1) + '' + Math.random());
9703                     
9704             } 
9705             
9706             
9707             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9708                 form:this.form.el.dom,
9709                 url:this.getUrl(!isPost),
9710                 method: method,
9711                 params:isPost ? this.getParams() : null,
9712                 isUpload: this.form.fileUpload,
9713                 formData : this.form.formData
9714             }));
9715             
9716             this.uploadProgress();
9717
9718         }else if (o.clientValidation !== false){ // client validation failed
9719             this.failureType = Roo.form.Action.CLIENT_INVALID;
9720             this.form.afterAction(this, false);
9721         }
9722     },
9723
9724     success : function(response)
9725     {
9726         this.uploadComplete= true;
9727         if (this.haveProgress) {
9728             Roo.MessageBox.hide();
9729         }
9730         
9731         
9732         var result = this.processResponse(response);
9733         if(result === true || result.success){
9734             this.form.afterAction(this, true);
9735             return;
9736         }
9737         if(result.errors){
9738             this.form.markInvalid(result.errors);
9739             this.failureType = Roo.form.Action.SERVER_INVALID;
9740         }
9741         this.form.afterAction(this, false);
9742     },
9743     failure : function(response)
9744     {
9745         this.uploadComplete= true;
9746         if (this.haveProgress) {
9747             Roo.MessageBox.hide();
9748         }
9749         
9750         this.response = response;
9751         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9752         this.form.afterAction(this, false);
9753     },
9754     
9755     handleResponse : function(response){
9756         if(this.form.errorReader){
9757             var rs = this.form.errorReader.read(response);
9758             var errors = [];
9759             if(rs.records){
9760                 for(var i = 0, len = rs.records.length; i < len; i++) {
9761                     var r = rs.records[i];
9762                     errors[i] = r.data;
9763                 }
9764             }
9765             if(errors.length < 1){
9766                 errors = null;
9767             }
9768             return {
9769                 success : rs.success,
9770                 errors : errors
9771             };
9772         }
9773         var ret = false;
9774         try {
9775             ret = Roo.decode(response.responseText);
9776         } catch (e) {
9777             ret = {
9778                 success: false,
9779                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9780                 errors : []
9781             };
9782         }
9783         return ret;
9784         
9785     }
9786 });
9787
9788
9789 Roo.form.Action.Load = function(form, options){
9790     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9791     this.reader = this.form.reader;
9792 };
9793
9794 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9795     type : 'load',
9796
9797     run : function(){
9798         
9799         Roo.Ajax.request(Roo.apply(
9800                 this.createCallback(), {
9801                     method:this.getMethod(),
9802                     url:this.getUrl(false),
9803                     params:this.getParams()
9804         }));
9805     },
9806
9807     success : function(response){
9808         
9809         var result = this.processResponse(response);
9810         if(result === true || !result.success || !result.data){
9811             this.failureType = Roo.form.Action.LOAD_FAILURE;
9812             this.form.afterAction(this, false);
9813             return;
9814         }
9815         this.form.clearInvalid();
9816         this.form.setValues(result.data);
9817         this.form.afterAction(this, true);
9818     },
9819
9820     handleResponse : function(response){
9821         if(this.form.reader){
9822             var rs = this.form.reader.read(response);
9823             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9824             return {
9825                 success : rs.success,
9826                 data : data
9827             };
9828         }
9829         return Roo.decode(response.responseText);
9830     }
9831 });
9832
9833 Roo.form.Action.ACTION_TYPES = {
9834     'load' : Roo.form.Action.Load,
9835     'submit' : Roo.form.Action.Submit
9836 };/*
9837  * - LGPL
9838  *
9839  * form
9840  *
9841  */
9842
9843 /**
9844  * @class Roo.bootstrap.Form
9845  * @extends Roo.bootstrap.Component
9846  * Bootstrap Form class
9847  * @cfg {String} method  GET | POST (default POST)
9848  * @cfg {String} labelAlign top | left (default top)
9849  * @cfg {String} align left  | right - for navbars
9850  * @cfg {Boolean} loadMask load mask when submit (default true)
9851
9852  *
9853  * @constructor
9854  * Create a new Form
9855  * @param {Object} config The config object
9856  */
9857
9858
9859 Roo.bootstrap.Form = function(config){
9860     
9861     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9862     
9863     Roo.bootstrap.Form.popover.apply();
9864     
9865     this.addEvents({
9866         /**
9867          * @event clientvalidation
9868          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9869          * @param {Form} this
9870          * @param {Boolean} valid true if the form has passed client-side validation
9871          */
9872         clientvalidation: true,
9873         /**
9874          * @event beforeaction
9875          * Fires before any action is performed. Return false to cancel the action.
9876          * @param {Form} this
9877          * @param {Action} action The action to be performed
9878          */
9879         beforeaction: true,
9880         /**
9881          * @event actionfailed
9882          * Fires when an action fails.
9883          * @param {Form} this
9884          * @param {Action} action The action that failed
9885          */
9886         actionfailed : true,
9887         /**
9888          * @event actioncomplete
9889          * Fires when an action is completed.
9890          * @param {Form} this
9891          * @param {Action} action The action that completed
9892          */
9893         actioncomplete : true
9894     });
9895 };
9896
9897 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9898
9899      /**
9900      * @cfg {String} method
9901      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9902      */
9903     method : 'POST',
9904     /**
9905      * @cfg {String} url
9906      * The URL to use for form actions if one isn't supplied in the action options.
9907      */
9908     /**
9909      * @cfg {Boolean} fileUpload
9910      * Set to true if this form is a file upload.
9911      */
9912
9913     /**
9914      * @cfg {Object} baseParams
9915      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9916      */
9917
9918     /**
9919      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9920      */
9921     timeout: 30,
9922     /**
9923      * @cfg {Sting} align (left|right) for navbar forms
9924      */
9925     align : 'left',
9926
9927     // private
9928     activeAction : null,
9929
9930     /**
9931      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9932      * element by passing it or its id or mask the form itself by passing in true.
9933      * @type Mixed
9934      */
9935     waitMsgTarget : false,
9936
9937     loadMask : true,
9938     
9939     /**
9940      * @cfg {Boolean} errorMask (true|false) default false
9941      */
9942     errorMask : false,
9943     
9944     /**
9945      * @cfg {Number} maskOffset Default 100
9946      */
9947     maskOffset : 100,
9948     
9949     /**
9950      * @cfg {Boolean} maskBody
9951      */
9952     maskBody : false,
9953
9954     getAutoCreate : function(){
9955
9956         var cfg = {
9957             tag: 'form',
9958             method : this.method || 'POST',
9959             id : this.id || Roo.id(),
9960             cls : ''
9961         };
9962         if (this.parent().xtype.match(/^Nav/)) {
9963             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9964
9965         }
9966
9967         if (this.labelAlign == 'left' ) {
9968             cfg.cls += ' form-horizontal';
9969         }
9970
9971
9972         return cfg;
9973     },
9974     initEvents : function()
9975     {
9976         this.el.on('submit', this.onSubmit, this);
9977         // this was added as random key presses on the form where triggering form submit.
9978         this.el.on('keypress', function(e) {
9979             if (e.getCharCode() != 13) {
9980                 return true;
9981             }
9982             // we might need to allow it for textareas.. and some other items.
9983             // check e.getTarget().
9984
9985             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9986                 return true;
9987             }
9988
9989             Roo.log("keypress blocked");
9990
9991             e.preventDefault();
9992             return false;
9993         });
9994         
9995     },
9996     // private
9997     onSubmit : function(e){
9998         e.stopEvent();
9999     },
10000
10001      /**
10002      * Returns true if client-side validation on the form is successful.
10003      * @return Boolean
10004      */
10005     isValid : function(){
10006         var items = this.getItems();
10007         var valid = true;
10008         var target = false;
10009         
10010         items.each(function(f){
10011             
10012             if(f.validate()){
10013                 return;
10014             }
10015             
10016             Roo.log('invalid field: ' + f.name);
10017             
10018             valid = false;
10019
10020             if(!target && f.el.isVisible(true)){
10021                 target = f;
10022             }
10023            
10024         });
10025         
10026         if(this.errorMask && !valid){
10027             Roo.bootstrap.Form.popover.mask(this, target);
10028         }
10029         
10030         return valid;
10031     },
10032     
10033     /**
10034      * Returns true if any fields in this form have changed since their original load.
10035      * @return Boolean
10036      */
10037     isDirty : function(){
10038         var dirty = false;
10039         var items = this.getItems();
10040         items.each(function(f){
10041            if(f.isDirty()){
10042                dirty = true;
10043                return false;
10044            }
10045            return true;
10046         });
10047         return dirty;
10048     },
10049      /**
10050      * Performs a predefined action (submit or load) or custom actions you define on this form.
10051      * @param {String} actionName The name of the action type
10052      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10053      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10054      * accept other config options):
10055      * <pre>
10056 Property          Type             Description
10057 ----------------  ---------------  ----------------------------------------------------------------------------------
10058 url               String           The url for the action (defaults to the form's url)
10059 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10060 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10061 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10062                                    validate the form on the client (defaults to false)
10063      * </pre>
10064      * @return {BasicForm} this
10065      */
10066     doAction : function(action, options){
10067         if(typeof action == 'string'){
10068             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10069         }
10070         if(this.fireEvent('beforeaction', this, action) !== false){
10071             this.beforeAction(action);
10072             action.run.defer(100, action);
10073         }
10074         return this;
10075     },
10076
10077     // private
10078     beforeAction : function(action){
10079         var o = action.options;
10080         
10081         if(this.loadMask){
10082             
10083             if(this.maskBody){
10084                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10085             } else {
10086                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10087             }
10088         }
10089         // not really supported yet.. ??
10090
10091         //if(this.waitMsgTarget === true){
10092         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10093         //}else if(this.waitMsgTarget){
10094         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10095         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10096         //}else {
10097         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10098        // }
10099
10100     },
10101
10102     // private
10103     afterAction : function(action, success){
10104         this.activeAction = null;
10105         var o = action.options;
10106
10107         if(this.loadMask){
10108             
10109             if(this.maskBody){
10110                 Roo.get(document.body).unmask();
10111             } else {
10112                 this.el.unmask();
10113             }
10114         }
10115         
10116         //if(this.waitMsgTarget === true){
10117 //            this.el.unmask();
10118         //}else if(this.waitMsgTarget){
10119         //    this.waitMsgTarget.unmask();
10120         //}else{
10121         //    Roo.MessageBox.updateProgress(1);
10122         //    Roo.MessageBox.hide();
10123        // }
10124         //
10125         if(success){
10126             if(o.reset){
10127                 this.reset();
10128             }
10129             Roo.callback(o.success, o.scope, [this, action]);
10130             this.fireEvent('actioncomplete', this, action);
10131
10132         }else{
10133
10134             // failure condition..
10135             // we have a scenario where updates need confirming.
10136             // eg. if a locking scenario exists..
10137             // we look for { errors : { needs_confirm : true }} in the response.
10138             if (
10139                 (typeof(action.result) != 'undefined')  &&
10140                 (typeof(action.result.errors) != 'undefined')  &&
10141                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10142            ){
10143                 var _t = this;
10144                 Roo.log("not supported yet");
10145                  /*
10146
10147                 Roo.MessageBox.confirm(
10148                     "Change requires confirmation",
10149                     action.result.errorMsg,
10150                     function(r) {
10151                         if (r != 'yes') {
10152                             return;
10153                         }
10154                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10155                     }
10156
10157                 );
10158                 */
10159
10160
10161                 return;
10162             }
10163
10164             Roo.callback(o.failure, o.scope, [this, action]);
10165             // show an error message if no failed handler is set..
10166             if (!this.hasListener('actionfailed')) {
10167                 Roo.log("need to add dialog support");
10168                 /*
10169                 Roo.MessageBox.alert("Error",
10170                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10171                         action.result.errorMsg :
10172                         "Saving Failed, please check your entries or try again"
10173                 );
10174                 */
10175             }
10176
10177             this.fireEvent('actionfailed', this, action);
10178         }
10179
10180     },
10181     /**
10182      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10183      * @param {String} id The value to search for
10184      * @return Field
10185      */
10186     findField : function(id){
10187         var items = this.getItems();
10188         var field = items.get(id);
10189         if(!field){
10190              items.each(function(f){
10191                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10192                     field = f;
10193                     return false;
10194                 }
10195                 return true;
10196             });
10197         }
10198         return field || null;
10199     },
10200      /**
10201      * Mark fields in this form invalid in bulk.
10202      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10203      * @return {BasicForm} this
10204      */
10205     markInvalid : function(errors){
10206         if(errors instanceof Array){
10207             for(var i = 0, len = errors.length; i < len; i++){
10208                 var fieldError = errors[i];
10209                 var f = this.findField(fieldError.id);
10210                 if(f){
10211                     f.markInvalid(fieldError.msg);
10212                 }
10213             }
10214         }else{
10215             var field, id;
10216             for(id in errors){
10217                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10218                     field.markInvalid(errors[id]);
10219                 }
10220             }
10221         }
10222         //Roo.each(this.childForms || [], function (f) {
10223         //    f.markInvalid(errors);
10224         //});
10225
10226         return this;
10227     },
10228
10229     /**
10230      * Set values for fields in this form in bulk.
10231      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10232      * @return {BasicForm} this
10233      */
10234     setValues : function(values){
10235         if(values instanceof Array){ // array of objects
10236             for(var i = 0, len = values.length; i < len; i++){
10237                 var v = values[i];
10238                 var f = this.findField(v.id);
10239                 if(f){
10240                     f.setValue(v.value);
10241                     if(this.trackResetOnLoad){
10242                         f.originalValue = f.getValue();
10243                     }
10244                 }
10245             }
10246         }else{ // object hash
10247             var field, id;
10248             for(id in values){
10249                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10250
10251                     if (field.setFromData &&
10252                         field.valueField &&
10253                         field.displayField &&
10254                         // combos' with local stores can
10255                         // be queried via setValue()
10256                         // to set their value..
10257                         (field.store && !field.store.isLocal)
10258                         ) {
10259                         // it's a combo
10260                         var sd = { };
10261                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10262                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10263                         field.setFromData(sd);
10264
10265                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10266                         
10267                         field.setFromData(values);
10268                         
10269                     } else {
10270                         field.setValue(values[id]);
10271                     }
10272
10273
10274                     if(this.trackResetOnLoad){
10275                         field.originalValue = field.getValue();
10276                     }
10277                 }
10278             }
10279         }
10280
10281         //Roo.each(this.childForms || [], function (f) {
10282         //    f.setValues(values);
10283         //});
10284
10285         return this;
10286     },
10287
10288     /**
10289      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10290      * they are returned as an array.
10291      * @param {Boolean} asString
10292      * @return {Object}
10293      */
10294     getValues : function(asString){
10295         //if (this.childForms) {
10296             // copy values from the child forms
10297         //    Roo.each(this.childForms, function (f) {
10298         //        this.setValues(f.getValues());
10299         //    }, this);
10300         //}
10301
10302
10303
10304         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10305         if(asString === true){
10306             return fs;
10307         }
10308         return Roo.urlDecode(fs);
10309     },
10310
10311     /**
10312      * Returns the fields in this form as an object with key/value pairs.
10313      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10314      * @return {Object}
10315      */
10316     getFieldValues : function(with_hidden)
10317     {
10318         var items = this.getItems();
10319         var ret = {};
10320         items.each(function(f){
10321             
10322             if (!f.getName()) {
10323                 return;
10324             }
10325             
10326             var v = f.getValue();
10327             
10328             if (f.inputType =='radio') {
10329                 if (typeof(ret[f.getName()]) == 'undefined') {
10330                     ret[f.getName()] = ''; // empty..
10331                 }
10332
10333                 if (!f.el.dom.checked) {
10334                     return;
10335
10336                 }
10337                 v = f.el.dom.value;
10338
10339             }
10340             
10341             if(f.xtype == 'MoneyField'){
10342                 ret[f.currencyName] = f.getCurrency();
10343             }
10344
10345             // not sure if this supported any more..
10346             if ((typeof(v) == 'object') && f.getRawValue) {
10347                 v = f.getRawValue() ; // dates..
10348             }
10349             // combo boxes where name != hiddenName...
10350             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10351                 ret[f.name] = f.getRawValue();
10352             }
10353             ret[f.getName()] = v;
10354         });
10355
10356         return ret;
10357     },
10358
10359     /**
10360      * Clears all invalid messages in this form.
10361      * @return {BasicForm} this
10362      */
10363     clearInvalid : function(){
10364         var items = this.getItems();
10365
10366         items.each(function(f){
10367            f.clearInvalid();
10368         });
10369
10370         return this;
10371     },
10372
10373     /**
10374      * Resets this form.
10375      * @return {BasicForm} this
10376      */
10377     reset : function(){
10378         var items = this.getItems();
10379         items.each(function(f){
10380             f.reset();
10381         });
10382
10383         Roo.each(this.childForms || [], function (f) {
10384             f.reset();
10385         });
10386
10387
10388         return this;
10389     },
10390     
10391     getItems : function()
10392     {
10393         var r=new Roo.util.MixedCollection(false, function(o){
10394             return o.id || (o.id = Roo.id());
10395         });
10396         var iter = function(el) {
10397             if (el.inputEl) {
10398                 r.add(el);
10399             }
10400             if (!el.items) {
10401                 return;
10402             }
10403             Roo.each(el.items,function(e) {
10404                 iter(e);
10405             });
10406         };
10407
10408         iter(this);
10409         return r;
10410     },
10411     
10412     hideFields : function(items)
10413     {
10414         Roo.each(items, function(i){
10415             
10416             var f = this.findField(i);
10417             
10418             if(!f){
10419                 return;
10420             }
10421             
10422             f.hide();
10423             
10424         }, this);
10425     },
10426     
10427     showFields : function(items)
10428     {
10429         Roo.each(items, function(i){
10430             
10431             var f = this.findField(i);
10432             
10433             if(!f){
10434                 return;
10435             }
10436             
10437             f.show();
10438             
10439         }, this);
10440     }
10441
10442 });
10443
10444 Roo.apply(Roo.bootstrap.Form, {
10445     
10446     popover : {
10447         
10448         padding : 5,
10449         
10450         isApplied : false,
10451         
10452         isMasked : false,
10453         
10454         form : false,
10455         
10456         target : false,
10457         
10458         toolTip : false,
10459         
10460         intervalID : false,
10461         
10462         maskEl : false,
10463         
10464         apply : function()
10465         {
10466             if(this.isApplied){
10467                 return;
10468             }
10469             
10470             this.maskEl = {
10471                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10472                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10473                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10474                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10475             };
10476             
10477             this.maskEl.top.enableDisplayMode("block");
10478             this.maskEl.left.enableDisplayMode("block");
10479             this.maskEl.bottom.enableDisplayMode("block");
10480             this.maskEl.right.enableDisplayMode("block");
10481             
10482             this.toolTip = new Roo.bootstrap.Tooltip({
10483                 cls : 'roo-form-error-popover',
10484                 alignment : {
10485                     'left' : ['r-l', [-2,0], 'right'],
10486                     'right' : ['l-r', [2,0], 'left'],
10487                     'bottom' : ['tl-bl', [0,2], 'top'],
10488                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10489                 }
10490             });
10491             
10492             this.toolTip.render(Roo.get(document.body));
10493
10494             this.toolTip.el.enableDisplayMode("block");
10495             
10496             Roo.get(document.body).on('click', function(){
10497                 this.unmask();
10498             }, this);
10499             
10500             Roo.get(document.body).on('touchstart', function(){
10501                 this.unmask();
10502             }, this);
10503             
10504             this.isApplied = true
10505         },
10506         
10507         mask : function(form, target)
10508         {
10509             this.form = form;
10510             
10511             this.target = target;
10512             
10513             if(!this.form.errorMask || !target.el){
10514                 return;
10515             }
10516             
10517             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10518             
10519             Roo.log(scrollable);
10520             
10521             var ot = this.target.el.calcOffsetsTo(scrollable);
10522             
10523             var scrollTo = ot[1] - this.form.maskOffset;
10524             
10525             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10526             
10527             scrollable.scrollTo('top', scrollTo);
10528             
10529             var box = this.target.el.getBox();
10530             Roo.log(box);
10531             var zIndex = Roo.bootstrap.Modal.zIndex++;
10532
10533             
10534             this.maskEl.top.setStyle('position', 'absolute');
10535             this.maskEl.top.setStyle('z-index', zIndex);
10536             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10537             this.maskEl.top.setLeft(0);
10538             this.maskEl.top.setTop(0);
10539             this.maskEl.top.show();
10540             
10541             this.maskEl.left.setStyle('position', 'absolute');
10542             this.maskEl.left.setStyle('z-index', zIndex);
10543             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10544             this.maskEl.left.setLeft(0);
10545             this.maskEl.left.setTop(box.y - this.padding);
10546             this.maskEl.left.show();
10547
10548             this.maskEl.bottom.setStyle('position', 'absolute');
10549             this.maskEl.bottom.setStyle('z-index', zIndex);
10550             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10551             this.maskEl.bottom.setLeft(0);
10552             this.maskEl.bottom.setTop(box.bottom + this.padding);
10553             this.maskEl.bottom.show();
10554
10555             this.maskEl.right.setStyle('position', 'absolute');
10556             this.maskEl.right.setStyle('z-index', zIndex);
10557             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10558             this.maskEl.right.setLeft(box.right + this.padding);
10559             this.maskEl.right.setTop(box.y - this.padding);
10560             this.maskEl.right.show();
10561
10562             this.toolTip.bindEl = this.target.el;
10563
10564             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10565
10566             var tip = this.target.blankText;
10567
10568             if(this.target.getValue() !== '' ) {
10569                 
10570                 if (this.target.invalidText.length) {
10571                     tip = this.target.invalidText;
10572                 } else if (this.target.regexText.length){
10573                     tip = this.target.regexText;
10574                 }
10575             }
10576
10577             this.toolTip.show(tip);
10578
10579             this.intervalID = window.setInterval(function() {
10580                 Roo.bootstrap.Form.popover.unmask();
10581             }, 10000);
10582
10583             window.onwheel = function(){ return false;};
10584             
10585             (function(){ this.isMasked = true; }).defer(500, this);
10586             
10587         },
10588         
10589         unmask : function()
10590         {
10591             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10592                 return;
10593             }
10594             
10595             this.maskEl.top.setStyle('position', 'absolute');
10596             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10597             this.maskEl.top.hide();
10598
10599             this.maskEl.left.setStyle('position', 'absolute');
10600             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10601             this.maskEl.left.hide();
10602
10603             this.maskEl.bottom.setStyle('position', 'absolute');
10604             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10605             this.maskEl.bottom.hide();
10606
10607             this.maskEl.right.setStyle('position', 'absolute');
10608             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10609             this.maskEl.right.hide();
10610             
10611             this.toolTip.hide();
10612             
10613             this.toolTip.el.hide();
10614             
10615             window.onwheel = function(){ return true;};
10616             
10617             if(this.intervalID){
10618                 window.clearInterval(this.intervalID);
10619                 this.intervalID = false;
10620             }
10621             
10622             this.isMasked = false;
10623             
10624         }
10625         
10626     }
10627     
10628 });
10629
10630 /*
10631  * Based on:
10632  * Ext JS Library 1.1.1
10633  * Copyright(c) 2006-2007, Ext JS, LLC.
10634  *
10635  * Originally Released Under LGPL - original licence link has changed is not relivant.
10636  *
10637  * Fork - LGPL
10638  * <script type="text/javascript">
10639  */
10640 /**
10641  * @class Roo.form.VTypes
10642  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10643  * @singleton
10644  */
10645 Roo.form.VTypes = function(){
10646     // closure these in so they are only created once.
10647     var alpha = /^[a-zA-Z_]+$/;
10648     var alphanum = /^[a-zA-Z0-9_]+$/;
10649     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10650     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10651
10652     // All these messages and functions are configurable
10653     return {
10654         /**
10655          * The function used to validate email addresses
10656          * @param {String} value The email address
10657          */
10658         'email' : function(v){
10659             return email.test(v);
10660         },
10661         /**
10662          * The error text to display when the email validation function returns false
10663          * @type String
10664          */
10665         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10666         /**
10667          * The keystroke filter mask to be applied on email input
10668          * @type RegExp
10669          */
10670         'emailMask' : /[a-z0-9_\.\-@]/i,
10671
10672         /**
10673          * The function used to validate URLs
10674          * @param {String} value The URL
10675          */
10676         'url' : function(v){
10677             return url.test(v);
10678         },
10679         /**
10680          * The error text to display when the url validation function returns false
10681          * @type String
10682          */
10683         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10684         
10685         /**
10686          * The function used to validate alpha values
10687          * @param {String} value The value
10688          */
10689         'alpha' : function(v){
10690             return alpha.test(v);
10691         },
10692         /**
10693          * The error text to display when the alpha validation function returns false
10694          * @type String
10695          */
10696         'alphaText' : 'This field should only contain letters and _',
10697         /**
10698          * The keystroke filter mask to be applied on alpha input
10699          * @type RegExp
10700          */
10701         'alphaMask' : /[a-z_]/i,
10702
10703         /**
10704          * The function used to validate alphanumeric values
10705          * @param {String} value The value
10706          */
10707         'alphanum' : function(v){
10708             return alphanum.test(v);
10709         },
10710         /**
10711          * The error text to display when the alphanumeric validation function returns false
10712          * @type String
10713          */
10714         'alphanumText' : 'This field should only contain letters, numbers and _',
10715         /**
10716          * The keystroke filter mask to be applied on alphanumeric input
10717          * @type RegExp
10718          */
10719         'alphanumMask' : /[a-z0-9_]/i
10720     };
10721 }();/*
10722  * - LGPL
10723  *
10724  * Input
10725  * 
10726  */
10727
10728 /**
10729  * @class Roo.bootstrap.Input
10730  * @extends Roo.bootstrap.Component
10731  * Bootstrap Input class
10732  * @cfg {Boolean} disabled is it disabled
10733  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10734  * @cfg {String} name name of the input
10735  * @cfg {string} fieldLabel - the label associated
10736  * @cfg {string} placeholder - placeholder to put in text.
10737  * @cfg {string}  before - input group add on before
10738  * @cfg {string} after - input group add on after
10739  * @cfg {string} size - (lg|sm) or leave empty..
10740  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10741  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10742  * @cfg {Number} md colspan out of 12 for computer-sized screens
10743  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10744  * @cfg {string} value default value of the input
10745  * @cfg {Number} labelWidth set the width of label 
10746  * @cfg {Number} labellg set the width of label (1-12)
10747  * @cfg {Number} labelmd set the width of label (1-12)
10748  * @cfg {Number} labelsm set the width of label (1-12)
10749  * @cfg {Number} labelxs set the width of label (1-12)
10750  * @cfg {String} labelAlign (top|left)
10751  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10752  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10753  * @cfg {String} indicatorpos (left|right) default left
10754  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10755  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10756  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10757
10758  * @cfg {String} align (left|center|right) Default left
10759  * @cfg {Boolean} forceFeedback (true|false) Default false
10760  * 
10761  * @constructor
10762  * Create a new Input
10763  * @param {Object} config The config object
10764  */
10765
10766 Roo.bootstrap.Input = function(config){
10767     
10768     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10769     
10770     this.addEvents({
10771         /**
10772          * @event focus
10773          * Fires when this field receives input focus.
10774          * @param {Roo.form.Field} this
10775          */
10776         focus : true,
10777         /**
10778          * @event blur
10779          * Fires when this field loses input focus.
10780          * @param {Roo.form.Field} this
10781          */
10782         blur : true,
10783         /**
10784          * @event specialkey
10785          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10786          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10787          * @param {Roo.form.Field} this
10788          * @param {Roo.EventObject} e The event object
10789          */
10790         specialkey : true,
10791         /**
10792          * @event change
10793          * Fires just before the field blurs if the field value has changed.
10794          * @param {Roo.form.Field} this
10795          * @param {Mixed} newValue The new value
10796          * @param {Mixed} oldValue The original value
10797          */
10798         change : true,
10799         /**
10800          * @event invalid
10801          * Fires after the field has been marked as invalid.
10802          * @param {Roo.form.Field} this
10803          * @param {String} msg The validation message
10804          */
10805         invalid : true,
10806         /**
10807          * @event valid
10808          * Fires after the field has been validated with no errors.
10809          * @param {Roo.form.Field} this
10810          */
10811         valid : true,
10812          /**
10813          * @event keyup
10814          * Fires after the key up
10815          * @param {Roo.form.Field} this
10816          * @param {Roo.EventObject}  e The event Object
10817          */
10818         keyup : true,
10819         /**
10820          * @event paste
10821          * Fires after the user pastes into input
10822          * @param {Roo.form.Field} this
10823          * @param {Roo.EventObject}  e The event Object
10824          */
10825         paste : true
10826     });
10827 };
10828
10829 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10830      /**
10831      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10832       automatic validation (defaults to "keyup").
10833      */
10834     validationEvent : "keyup",
10835      /**
10836      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10837      */
10838     validateOnBlur : true,
10839     /**
10840      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10841      */
10842     validationDelay : 250,
10843      /**
10844      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10845      */
10846     focusClass : "x-form-focus",  // not needed???
10847     
10848        
10849     /**
10850      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10851      */
10852     invalidClass : "has-warning",
10853     
10854     /**
10855      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10856      */
10857     validClass : "has-success",
10858     
10859     /**
10860      * @cfg {Boolean} hasFeedback (true|false) default true
10861      */
10862     hasFeedback : true,
10863     
10864     /**
10865      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10866      */
10867     invalidFeedbackClass : "glyphicon-warning-sign",
10868     
10869     /**
10870      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10871      */
10872     validFeedbackClass : "glyphicon-ok",
10873     
10874     /**
10875      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10876      */
10877     selectOnFocus : false,
10878     
10879      /**
10880      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10881      */
10882     maskRe : null,
10883        /**
10884      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10885      */
10886     vtype : null,
10887     
10888       /**
10889      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10890      */
10891     disableKeyFilter : false,
10892     
10893        /**
10894      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10895      */
10896     disabled : false,
10897      /**
10898      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10899      */
10900     allowBlank : true,
10901     /**
10902      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10903      */
10904     blankText : "Please complete this mandatory field",
10905     
10906      /**
10907      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10908      */
10909     minLength : 0,
10910     /**
10911      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10912      */
10913     maxLength : Number.MAX_VALUE,
10914     /**
10915      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10916      */
10917     minLengthText : "The minimum length for this field is {0}",
10918     /**
10919      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10920      */
10921     maxLengthText : "The maximum length for this field is {0}",
10922   
10923     
10924     /**
10925      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10926      * If available, this function will be called only after the basic validators all return true, and will be passed the
10927      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10928      */
10929     validator : null,
10930     /**
10931      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10932      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10933      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10934      */
10935     regex : null,
10936     /**
10937      * @cfg {String} regexText -- Depricated - use Invalid Text
10938      */
10939     regexText : "",
10940     
10941     /**
10942      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10943      */
10944     invalidText : "",
10945     
10946     
10947     
10948     autocomplete: false,
10949     
10950     
10951     fieldLabel : '',
10952     inputType : 'text',
10953     
10954     name : false,
10955     placeholder: false,
10956     before : false,
10957     after : false,
10958     size : false,
10959     hasFocus : false,
10960     preventMark: false,
10961     isFormField : true,
10962     value : '',
10963     labelWidth : 2,
10964     labelAlign : false,
10965     readOnly : false,
10966     align : false,
10967     formatedValue : false,
10968     forceFeedback : false,
10969     
10970     indicatorpos : 'left',
10971     
10972     labellg : 0,
10973     labelmd : 0,
10974     labelsm : 0,
10975     labelxs : 0,
10976     
10977     capture : '',
10978     accept : '',
10979     
10980     parentLabelAlign : function()
10981     {
10982         var parent = this;
10983         while (parent.parent()) {
10984             parent = parent.parent();
10985             if (typeof(parent.labelAlign) !='undefined') {
10986                 return parent.labelAlign;
10987             }
10988         }
10989         return 'left';
10990         
10991     },
10992     
10993     getAutoCreate : function()
10994     {
10995         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10996         
10997         var id = Roo.id();
10998         
10999         var cfg = {};
11000         
11001         if(this.inputType != 'hidden'){
11002             cfg.cls = 'form-group' //input-group
11003         }
11004         
11005         var input =  {
11006             tag: 'input',
11007             id : id,
11008             type : this.inputType,
11009             value : this.value,
11010             cls : 'form-control',
11011             placeholder : this.placeholder || '',
11012             autocomplete : this.autocomplete || 'new-password'
11013         };
11014         if (this.inputType == 'file') {
11015             input.style = 'overflow:hidden'; // why not in CSS?
11016         }
11017         
11018         if(this.capture.length){
11019             input.capture = this.capture;
11020         }
11021         
11022         if(this.accept.length){
11023             input.accept = this.accept + "/*";
11024         }
11025         
11026         if(this.align){
11027             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11028         }
11029         
11030         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11031             input.maxLength = this.maxLength;
11032         }
11033         
11034         if (this.disabled) {
11035             input.disabled=true;
11036         }
11037         
11038         if (this.readOnly) {
11039             input.readonly=true;
11040         }
11041         
11042         if (this.name) {
11043             input.name = this.name;
11044         }
11045         
11046         if (this.size) {
11047             input.cls += ' input-' + this.size;
11048         }
11049         
11050         var settings=this;
11051         ['xs','sm','md','lg'].map(function(size){
11052             if (settings[size]) {
11053                 cfg.cls += ' col-' + size + '-' + settings[size];
11054             }
11055         });
11056         
11057         var inputblock = input;
11058         
11059         var feedback = {
11060             tag: 'span',
11061             cls: 'glyphicon form-control-feedback'
11062         };
11063             
11064         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11065             
11066             inputblock = {
11067                 cls : 'has-feedback',
11068                 cn :  [
11069                     input,
11070                     feedback
11071                 ] 
11072             };  
11073         }
11074         
11075         if (this.before || this.after) {
11076             
11077             inputblock = {
11078                 cls : 'input-group',
11079                 cn :  [] 
11080             };
11081             
11082             if (this.before && typeof(this.before) == 'string') {
11083                 
11084                 inputblock.cn.push({
11085                     tag :'span',
11086                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11087                     html : this.before
11088                 });
11089             }
11090             if (this.before && typeof(this.before) == 'object') {
11091                 this.before = Roo.factory(this.before);
11092                 
11093                 inputblock.cn.push({
11094                     tag :'span',
11095                     cls : 'roo-input-before input-group-prepend   input-group-' +
11096                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11097                 });
11098             }
11099             
11100             inputblock.cn.push(input);
11101             
11102             if (this.after && typeof(this.after) == 'string') {
11103                 inputblock.cn.push({
11104                     tag :'span',
11105                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11106                     html : this.after
11107                 });
11108             }
11109             if (this.after && typeof(this.after) == 'object') {
11110                 this.after = Roo.factory(this.after);
11111                 
11112                 inputblock.cn.push({
11113                     tag :'span',
11114                     cls : 'roo-input-after input-group-append  input-group-' +
11115                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11116                 });
11117             }
11118             
11119             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11120                 inputblock.cls += ' has-feedback';
11121                 inputblock.cn.push(feedback);
11122             }
11123         };
11124         var indicator = {
11125             tag : 'i',
11126             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11127             tooltip : 'This field is required'
11128         };
11129         if (this.allowBlank ) {
11130             indicator.style = this.allowBlank ? ' display:none' : '';
11131         }
11132         if (align ==='left' && this.fieldLabel.length) {
11133             
11134             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11135             
11136             cfg.cn = [
11137                 indicator,
11138                 {
11139                     tag: 'label',
11140                     'for' :  id,
11141                     cls : 'control-label col-form-label',
11142                     html : this.fieldLabel
11143
11144                 },
11145                 {
11146                     cls : "", 
11147                     cn: [
11148                         inputblock
11149                     ]
11150                 }
11151             ];
11152             
11153             var labelCfg = cfg.cn[1];
11154             var contentCfg = cfg.cn[2];
11155             
11156             if(this.indicatorpos == 'right'){
11157                 cfg.cn = [
11158                     {
11159                         tag: 'label',
11160                         'for' :  id,
11161                         cls : 'control-label col-form-label',
11162                         cn : [
11163                             {
11164                                 tag : 'span',
11165                                 html : this.fieldLabel
11166                             },
11167                             indicator
11168                         ]
11169                     },
11170                     {
11171                         cls : "",
11172                         cn: [
11173                             inputblock
11174                         ]
11175                     }
11176
11177                 ];
11178                 
11179                 labelCfg = cfg.cn[0];
11180                 contentCfg = cfg.cn[1];
11181             
11182             }
11183             
11184             if(this.labelWidth > 12){
11185                 labelCfg.style = "width: " + this.labelWidth + 'px';
11186             }
11187             
11188             if(this.labelWidth < 13 && this.labelmd == 0){
11189                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11190             }
11191             
11192             if(this.labellg > 0){
11193                 labelCfg.cls += ' col-lg-' + this.labellg;
11194                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11195             }
11196             
11197             if(this.labelmd > 0){
11198                 labelCfg.cls += ' col-md-' + this.labelmd;
11199                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11200             }
11201             
11202             if(this.labelsm > 0){
11203                 labelCfg.cls += ' col-sm-' + this.labelsm;
11204                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11205             }
11206             
11207             if(this.labelxs > 0){
11208                 labelCfg.cls += ' col-xs-' + this.labelxs;
11209                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11210             }
11211             
11212             
11213         } else if ( this.fieldLabel.length) {
11214                 
11215             
11216             
11217             cfg.cn = [
11218                 {
11219                     tag : 'i',
11220                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11221                     tooltip : 'This field is required',
11222                     style : this.allowBlank ? ' display:none' : '' 
11223                 },
11224                 {
11225                     tag: 'label',
11226                    //cls : 'input-group-addon',
11227                     html : this.fieldLabel
11228
11229                 },
11230
11231                inputblock
11232
11233            ];
11234            
11235            if(this.indicatorpos == 'right'){
11236        
11237                 cfg.cn = [
11238                     {
11239                         tag: 'label',
11240                        //cls : 'input-group-addon',
11241                         html : this.fieldLabel
11242
11243                     },
11244                     {
11245                         tag : 'i',
11246                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11247                         tooltip : 'This field is required',
11248                         style : this.allowBlank ? ' display:none' : '' 
11249                     },
11250
11251                    inputblock
11252
11253                ];
11254
11255             }
11256
11257         } else {
11258             
11259             cfg.cn = [
11260
11261                     inputblock
11262
11263             ];
11264                 
11265                 
11266         };
11267         
11268         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11269            cfg.cls += ' navbar-form';
11270         }
11271         
11272         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11273             // on BS4 we do this only if not form 
11274             cfg.cls += ' navbar-form';
11275             cfg.tag = 'li';
11276         }
11277         
11278         return cfg;
11279         
11280     },
11281     /**
11282      * return the real input element.
11283      */
11284     inputEl: function ()
11285     {
11286         return this.el.select('input.form-control',true).first();
11287     },
11288     
11289     tooltipEl : function()
11290     {
11291         return this.inputEl();
11292     },
11293     
11294     indicatorEl : function()
11295     {
11296         if (Roo.bootstrap.version == 4) {
11297             return false; // not enabled in v4 yet.
11298         }
11299         
11300         var indicator = this.el.select('i.roo-required-indicator',true).first();
11301         
11302         if(!indicator){
11303             return false;
11304         }
11305         
11306         return indicator;
11307         
11308     },
11309     
11310     setDisabled : function(v)
11311     {
11312         var i  = this.inputEl().dom;
11313         if (!v) {
11314             i.removeAttribute('disabled');
11315             return;
11316             
11317         }
11318         i.setAttribute('disabled','true');
11319     },
11320     initEvents : function()
11321     {
11322           
11323         this.inputEl().on("keydown" , this.fireKey,  this);
11324         this.inputEl().on("focus", this.onFocus,  this);
11325         this.inputEl().on("blur", this.onBlur,  this);
11326         
11327         this.inputEl().relayEvent('keyup', this);
11328         this.inputEl().relayEvent('paste', this);
11329         
11330         this.indicator = this.indicatorEl();
11331         
11332         if(this.indicator){
11333             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11334         }
11335  
11336         // reference to original value for reset
11337         this.originalValue = this.getValue();
11338         //Roo.form.TextField.superclass.initEvents.call(this);
11339         if(this.validationEvent == 'keyup'){
11340             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11341             this.inputEl().on('keyup', this.filterValidation, this);
11342         }
11343         else if(this.validationEvent !== false){
11344             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11345         }
11346         
11347         if(this.selectOnFocus){
11348             this.on("focus", this.preFocus, this);
11349             
11350         }
11351         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11352             this.inputEl().on("keypress", this.filterKeys, this);
11353         } else {
11354             this.inputEl().relayEvent('keypress', this);
11355         }
11356        /* if(this.grow){
11357             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11358             this.el.on("click", this.autoSize,  this);
11359         }
11360         */
11361         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11362             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11363         }
11364         
11365         if (typeof(this.before) == 'object') {
11366             this.before.render(this.el.select('.roo-input-before',true).first());
11367         }
11368         if (typeof(this.after) == 'object') {
11369             this.after.render(this.el.select('.roo-input-after',true).first());
11370         }
11371         
11372         this.inputEl().on('change', this.onChange, this);
11373         
11374     },
11375     filterValidation : function(e){
11376         if(!e.isNavKeyPress()){
11377             this.validationTask.delay(this.validationDelay);
11378         }
11379     },
11380      /**
11381      * Validates the field value
11382      * @return {Boolean} True if the value is valid, else false
11383      */
11384     validate : function(){
11385         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11386         if(this.disabled || this.validateValue(this.getRawValue())){
11387             this.markValid();
11388             return true;
11389         }
11390         
11391         this.markInvalid();
11392         return false;
11393     },
11394     
11395     
11396     /**
11397      * Validates a value according to the field's validation rules and marks the field as invalid
11398      * if the validation fails
11399      * @param {Mixed} value The value to validate
11400      * @return {Boolean} True if the value is valid, else false
11401      */
11402     validateValue : function(value)
11403     {
11404         if(this.getVisibilityEl().hasClass('hidden')){
11405             return true;
11406         }
11407         
11408         if(value.length < 1)  { // if it's blank
11409             if(this.allowBlank){
11410                 return true;
11411             }
11412             return false;
11413         }
11414         
11415         if(value.length < this.minLength){
11416             return false;
11417         }
11418         if(value.length > this.maxLength){
11419             return false;
11420         }
11421         if(this.vtype){
11422             var vt = Roo.form.VTypes;
11423             if(!vt[this.vtype](value, this)){
11424                 return false;
11425             }
11426         }
11427         if(typeof this.validator == "function"){
11428             var msg = this.validator(value);
11429             if(msg !== true){
11430                 return false;
11431             }
11432             if (typeof(msg) == 'string') {
11433                 this.invalidText = msg;
11434             }
11435         }
11436         
11437         if(this.regex && !this.regex.test(value)){
11438             return false;
11439         }
11440         
11441         return true;
11442     },
11443     
11444      // private
11445     fireKey : function(e){
11446         //Roo.log('field ' + e.getKey());
11447         if(e.isNavKeyPress()){
11448             this.fireEvent("specialkey", this, e);
11449         }
11450     },
11451     focus : function (selectText){
11452         if(this.rendered){
11453             this.inputEl().focus();
11454             if(selectText === true){
11455                 this.inputEl().dom.select();
11456             }
11457         }
11458         return this;
11459     } ,
11460     
11461     onFocus : function(){
11462         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11463            // this.el.addClass(this.focusClass);
11464         }
11465         if(!this.hasFocus){
11466             this.hasFocus = true;
11467             this.startValue = this.getValue();
11468             this.fireEvent("focus", this);
11469         }
11470     },
11471     
11472     beforeBlur : Roo.emptyFn,
11473
11474     
11475     // private
11476     onBlur : function(){
11477         this.beforeBlur();
11478         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11479             //this.el.removeClass(this.focusClass);
11480         }
11481         this.hasFocus = false;
11482         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11483             this.validate();
11484         }
11485         var v = this.getValue();
11486         if(String(v) !== String(this.startValue)){
11487             this.fireEvent('change', this, v, this.startValue);
11488         }
11489         this.fireEvent("blur", this);
11490     },
11491     
11492     onChange : function(e)
11493     {
11494         var v = this.getValue();
11495         if(String(v) !== String(this.startValue)){
11496             this.fireEvent('change', this, v, this.startValue);
11497         }
11498         
11499     },
11500     
11501     /**
11502      * Resets the current field value to the originally loaded value and clears any validation messages
11503      */
11504     reset : function(){
11505         this.setValue(this.originalValue);
11506         this.validate();
11507     },
11508      /**
11509      * Returns the name of the field
11510      * @return {Mixed} name The name field
11511      */
11512     getName: function(){
11513         return this.name;
11514     },
11515      /**
11516      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11517      * @return {Mixed} value The field value
11518      */
11519     getValue : function(){
11520         
11521         var v = this.inputEl().getValue();
11522         
11523         return v;
11524     },
11525     /**
11526      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11527      * @return {Mixed} value The field value
11528      */
11529     getRawValue : function(){
11530         var v = this.inputEl().getValue();
11531         
11532         return v;
11533     },
11534     
11535     /**
11536      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11537      * @param {Mixed} value The value to set
11538      */
11539     setRawValue : function(v){
11540         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11541     },
11542     
11543     selectText : function(start, end){
11544         var v = this.getRawValue();
11545         if(v.length > 0){
11546             start = start === undefined ? 0 : start;
11547             end = end === undefined ? v.length : end;
11548             var d = this.inputEl().dom;
11549             if(d.setSelectionRange){
11550                 d.setSelectionRange(start, end);
11551             }else if(d.createTextRange){
11552                 var range = d.createTextRange();
11553                 range.moveStart("character", start);
11554                 range.moveEnd("character", v.length-end);
11555                 range.select();
11556             }
11557         }
11558     },
11559     
11560     /**
11561      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11562      * @param {Mixed} value The value to set
11563      */
11564     setValue : function(v){
11565         this.value = v;
11566         if(this.rendered){
11567             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11568             this.validate();
11569         }
11570     },
11571     
11572     /*
11573     processValue : function(value){
11574         if(this.stripCharsRe){
11575             var newValue = value.replace(this.stripCharsRe, '');
11576             if(newValue !== value){
11577                 this.setRawValue(newValue);
11578                 return newValue;
11579             }
11580         }
11581         return value;
11582     },
11583   */
11584     preFocus : function(){
11585         
11586         if(this.selectOnFocus){
11587             this.inputEl().dom.select();
11588         }
11589     },
11590     filterKeys : function(e){
11591         var k = e.getKey();
11592         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11593             return;
11594         }
11595         var c = e.getCharCode(), cc = String.fromCharCode(c);
11596         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11597             return;
11598         }
11599         if(!this.maskRe.test(cc)){
11600             e.stopEvent();
11601         }
11602     },
11603      /**
11604      * Clear any invalid styles/messages for this field
11605      */
11606     clearInvalid : function(){
11607         
11608         if(!this.el || this.preventMark){ // not rendered
11609             return;
11610         }
11611         
11612         
11613         this.el.removeClass([this.invalidClass, 'is-invalid']);
11614         
11615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11616             
11617             var feedback = this.el.select('.form-control-feedback', true).first();
11618             
11619             if(feedback){
11620                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11621             }
11622             
11623         }
11624         
11625         if(this.indicator){
11626             this.indicator.removeClass('visible');
11627             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11628         }
11629         
11630         this.fireEvent('valid', this);
11631     },
11632     
11633      /**
11634      * Mark this field as valid
11635      */
11636     markValid : function()
11637     {
11638         if(!this.el  || this.preventMark){ // not rendered...
11639             return;
11640         }
11641         
11642         this.el.removeClass([this.invalidClass, this.validClass]);
11643         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11644
11645         var feedback = this.el.select('.form-control-feedback', true).first();
11646             
11647         if(feedback){
11648             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649         }
11650         
11651         if(this.indicator){
11652             this.indicator.removeClass('visible');
11653             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11654         }
11655         
11656         if(this.disabled){
11657             return;
11658         }
11659         
11660            
11661         if(this.allowBlank && !this.getRawValue().length){
11662             return;
11663         }
11664         if (Roo.bootstrap.version == 3) {
11665             this.el.addClass(this.validClass);
11666         } else {
11667             this.inputEl().addClass('is-valid');
11668         }
11669
11670         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11671             
11672             var feedback = this.el.select('.form-control-feedback', true).first();
11673             
11674             if(feedback){
11675                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11676                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11677             }
11678             
11679         }
11680         
11681         this.fireEvent('valid', this);
11682     },
11683     
11684      /**
11685      * Mark this field as invalid
11686      * @param {String} msg The validation message
11687      */
11688     markInvalid : function(msg)
11689     {
11690         if(!this.el  || this.preventMark){ // not rendered
11691             return;
11692         }
11693         
11694         this.el.removeClass([this.invalidClass, this.validClass]);
11695         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11696         
11697         var feedback = this.el.select('.form-control-feedback', true).first();
11698             
11699         if(feedback){
11700             this.el.select('.form-control-feedback', true).first().removeClass(
11701                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11702         }
11703
11704         if(this.disabled){
11705             return;
11706         }
11707         
11708         if(this.allowBlank && !this.getRawValue().length){
11709             return;
11710         }
11711         
11712         if(this.indicator){
11713             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11714             this.indicator.addClass('visible');
11715         }
11716         if (Roo.bootstrap.version == 3) {
11717             this.el.addClass(this.invalidClass);
11718         } else {
11719             this.inputEl().addClass('is-invalid');
11720         }
11721         
11722         
11723         
11724         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11725             
11726             var feedback = this.el.select('.form-control-feedback', true).first();
11727             
11728             if(feedback){
11729                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11730                 
11731                 if(this.getValue().length || this.forceFeedback){
11732                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11733                 }
11734                 
11735             }
11736             
11737         }
11738         
11739         this.fireEvent('invalid', this, msg);
11740     },
11741     // private
11742     SafariOnKeyDown : function(event)
11743     {
11744         // this is a workaround for a password hang bug on chrome/ webkit.
11745         if (this.inputEl().dom.type != 'password') {
11746             return;
11747         }
11748         
11749         var isSelectAll = false;
11750         
11751         if(this.inputEl().dom.selectionEnd > 0){
11752             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11753         }
11754         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11755             event.preventDefault();
11756             this.setValue('');
11757             return;
11758         }
11759         
11760         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11761             
11762             event.preventDefault();
11763             // this is very hacky as keydown always get's upper case.
11764             //
11765             var cc = String.fromCharCode(event.getCharCode());
11766             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11767             
11768         }
11769     },
11770     adjustWidth : function(tag, w){
11771         tag = tag.toLowerCase();
11772         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11773             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11774                 if(tag == 'input'){
11775                     return w + 2;
11776                 }
11777                 if(tag == 'textarea'){
11778                     return w-2;
11779                 }
11780             }else if(Roo.isOpera){
11781                 if(tag == 'input'){
11782                     return w + 2;
11783                 }
11784                 if(tag == 'textarea'){
11785                     return w-2;
11786                 }
11787             }
11788         }
11789         return w;
11790     },
11791     
11792     setFieldLabel : function(v)
11793     {
11794         if(!this.rendered){
11795             return;
11796         }
11797         
11798         if(this.indicatorEl()){
11799             var ar = this.el.select('label > span',true);
11800             
11801             if (ar.elements.length) {
11802                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11803                 this.fieldLabel = v;
11804                 return;
11805             }
11806             
11807             var br = this.el.select('label',true);
11808             
11809             if(br.elements.length) {
11810                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11811                 this.fieldLabel = v;
11812                 return;
11813             }
11814             
11815             Roo.log('Cannot Found any of label > span || label in input');
11816             return;
11817         }
11818         
11819         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11820         this.fieldLabel = v;
11821         
11822         
11823     }
11824 });
11825
11826  
11827 /*
11828  * - LGPL
11829  *
11830  * Input
11831  * 
11832  */
11833
11834 /**
11835  * @class Roo.bootstrap.TextArea
11836  * @extends Roo.bootstrap.Input
11837  * Bootstrap TextArea class
11838  * @cfg {Number} cols Specifies the visible width of a text area
11839  * @cfg {Number} rows Specifies the visible number of lines in a text area
11840  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11841  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11842  * @cfg {string} html text
11843  * 
11844  * @constructor
11845  * Create a new TextArea
11846  * @param {Object} config The config object
11847  */
11848
11849 Roo.bootstrap.TextArea = function(config){
11850     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11851    
11852 };
11853
11854 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11855      
11856     cols : false,
11857     rows : 5,
11858     readOnly : false,
11859     warp : 'soft',
11860     resize : false,
11861     value: false,
11862     html: false,
11863     
11864     getAutoCreate : function(){
11865         
11866         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11867         
11868         var id = Roo.id();
11869         
11870         var cfg = {};
11871         
11872         if(this.inputType != 'hidden'){
11873             cfg.cls = 'form-group' //input-group
11874         }
11875         
11876         var input =  {
11877             tag: 'textarea',
11878             id : id,
11879             warp : this.warp,
11880             rows : this.rows,
11881             value : this.value || '',
11882             html: this.html || '',
11883             cls : 'form-control',
11884             placeholder : this.placeholder || '' 
11885             
11886         };
11887         
11888         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11889             input.maxLength = this.maxLength;
11890         }
11891         
11892         if(this.resize){
11893             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11894         }
11895         
11896         if(this.cols){
11897             input.cols = this.cols;
11898         }
11899         
11900         if (this.readOnly) {
11901             input.readonly = true;
11902         }
11903         
11904         if (this.name) {
11905             input.name = this.name;
11906         }
11907         
11908         if (this.size) {
11909             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11910         }
11911         
11912         var settings=this;
11913         ['xs','sm','md','lg'].map(function(size){
11914             if (settings[size]) {
11915                 cfg.cls += ' col-' + size + '-' + settings[size];
11916             }
11917         });
11918         
11919         var inputblock = input;
11920         
11921         if(this.hasFeedback && !this.allowBlank){
11922             
11923             var feedback = {
11924                 tag: 'span',
11925                 cls: 'glyphicon form-control-feedback'
11926             };
11927
11928             inputblock = {
11929                 cls : 'has-feedback',
11930                 cn :  [
11931                     input,
11932                     feedback
11933                 ] 
11934             };  
11935         }
11936         
11937         
11938         if (this.before || this.after) {
11939             
11940             inputblock = {
11941                 cls : 'input-group',
11942                 cn :  [] 
11943             };
11944             if (this.before) {
11945                 inputblock.cn.push({
11946                     tag :'span',
11947                     cls : 'input-group-addon',
11948                     html : this.before
11949                 });
11950             }
11951             
11952             inputblock.cn.push(input);
11953             
11954             if(this.hasFeedback && !this.allowBlank){
11955                 inputblock.cls += ' has-feedback';
11956                 inputblock.cn.push(feedback);
11957             }
11958             
11959             if (this.after) {
11960                 inputblock.cn.push({
11961                     tag :'span',
11962                     cls : 'input-group-addon',
11963                     html : this.after
11964                 });
11965             }
11966             
11967         }
11968         
11969         if (align ==='left' && this.fieldLabel.length) {
11970             cfg.cn = [
11971                 {
11972                     tag: 'label',
11973                     'for' :  id,
11974                     cls : 'control-label',
11975                     html : this.fieldLabel
11976                 },
11977                 {
11978                     cls : "",
11979                     cn: [
11980                         inputblock
11981                     ]
11982                 }
11983
11984             ];
11985             
11986             if(this.labelWidth > 12){
11987                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11988             }
11989
11990             if(this.labelWidth < 13 && this.labelmd == 0){
11991                 this.labelmd = this.labelWidth;
11992             }
11993
11994             if(this.labellg > 0){
11995                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11996                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11997             }
11998
11999             if(this.labelmd > 0){
12000                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12001                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12002             }
12003
12004             if(this.labelsm > 0){
12005                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12006                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12007             }
12008
12009             if(this.labelxs > 0){
12010                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12011                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12012             }
12013             
12014         } else if ( this.fieldLabel.length) {
12015             cfg.cn = [
12016
12017                {
12018                    tag: 'label',
12019                    //cls : 'input-group-addon',
12020                    html : this.fieldLabel
12021
12022                },
12023
12024                inputblock
12025
12026            ];
12027
12028         } else {
12029
12030             cfg.cn = [
12031
12032                 inputblock
12033
12034             ];
12035                 
12036         }
12037         
12038         if (this.disabled) {
12039             input.disabled=true;
12040         }
12041         
12042         return cfg;
12043         
12044     },
12045     /**
12046      * return the real textarea element.
12047      */
12048     inputEl: function ()
12049     {
12050         return this.el.select('textarea.form-control',true).first();
12051     },
12052     
12053     /**
12054      * Clear any invalid styles/messages for this field
12055      */
12056     clearInvalid : function()
12057     {
12058         
12059         if(!this.el || this.preventMark){ // not rendered
12060             return;
12061         }
12062         
12063         var label = this.el.select('label', true).first();
12064         var icon = this.el.select('i.fa-star', true).first();
12065         
12066         if(label && icon){
12067             icon.remove();
12068         }
12069         this.el.removeClass( this.validClass);
12070         this.inputEl().removeClass('is-invalid');
12071          
12072         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12073             
12074             var feedback = this.el.select('.form-control-feedback', true).first();
12075             
12076             if(feedback){
12077                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12078             }
12079             
12080         }
12081         
12082         this.fireEvent('valid', this);
12083     },
12084     
12085      /**
12086      * Mark this field as valid
12087      */
12088     markValid : function()
12089     {
12090         if(!this.el  || this.preventMark){ // not rendered
12091             return;
12092         }
12093         
12094         this.el.removeClass([this.invalidClass, this.validClass]);
12095         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12096         
12097         var feedback = this.el.select('.form-control-feedback', true).first();
12098             
12099         if(feedback){
12100             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12101         }
12102
12103         if(this.disabled || this.allowBlank){
12104             return;
12105         }
12106         
12107         var label = this.el.select('label', true).first();
12108         var icon = this.el.select('i.fa-star', true).first();
12109         
12110         if(label && icon){
12111             icon.remove();
12112         }
12113         if (Roo.bootstrap.version == 3) {
12114             this.el.addClass(this.validClass);
12115         } else {
12116             this.inputEl().addClass('is-valid');
12117         }
12118         
12119         
12120         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12121             
12122             var feedback = this.el.select('.form-control-feedback', true).first();
12123             
12124             if(feedback){
12125                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12126                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12127             }
12128             
12129         }
12130         
12131         this.fireEvent('valid', this);
12132     },
12133     
12134      /**
12135      * Mark this field as invalid
12136      * @param {String} msg The validation message
12137      */
12138     markInvalid : function(msg)
12139     {
12140         if(!this.el  || this.preventMark){ // not rendered
12141             return;
12142         }
12143         
12144         this.el.removeClass([this.invalidClass, this.validClass]);
12145         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12146         
12147         var feedback = this.el.select('.form-control-feedback', true).first();
12148             
12149         if(feedback){
12150             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12151         }
12152
12153         if(this.disabled || this.allowBlank){
12154             return;
12155         }
12156         
12157         var label = this.el.select('label', true).first();
12158         var icon = this.el.select('i.fa-star', true).first();
12159         
12160         if(!this.getValue().length && label && !icon){
12161             this.el.createChild({
12162                 tag : 'i',
12163                 cls : 'text-danger fa fa-lg fa-star',
12164                 tooltip : 'This field is required',
12165                 style : 'margin-right:5px;'
12166             }, label, true);
12167         }
12168         
12169         if (Roo.bootstrap.version == 3) {
12170             this.el.addClass(this.invalidClass);
12171         } else {
12172             this.inputEl().addClass('is-invalid');
12173         }
12174         
12175         // fixme ... this may be depricated need to test..
12176         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12177             
12178             var feedback = this.el.select('.form-control-feedback', true).first();
12179             
12180             if(feedback){
12181                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12182                 
12183                 if(this.getValue().length || this.forceFeedback){
12184                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12185                 }
12186                 
12187             }
12188             
12189         }
12190         
12191         this.fireEvent('invalid', this, msg);
12192     }
12193 });
12194
12195  
12196 /*
12197  * - LGPL
12198  *
12199  * trigger field - base class for combo..
12200  * 
12201  */
12202  
12203 /**
12204  * @class Roo.bootstrap.TriggerField
12205  * @extends Roo.bootstrap.Input
12206  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12207  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12208  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12209  * for which you can provide a custom implementation.  For example:
12210  * <pre><code>
12211 var trigger = new Roo.bootstrap.TriggerField();
12212 trigger.onTriggerClick = myTriggerFn;
12213 trigger.applyTo('my-field');
12214 </code></pre>
12215  *
12216  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12217  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12218  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12219  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12220  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12221
12222  * @constructor
12223  * Create a new TriggerField.
12224  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12225  * to the base TextField)
12226  */
12227 Roo.bootstrap.TriggerField = function(config){
12228     this.mimicing = false;
12229     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12230 };
12231
12232 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12233     /**
12234      * @cfg {String} triggerClass A CSS class to apply to the trigger
12235      */
12236      /**
12237      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12238      */
12239     hideTrigger:false,
12240
12241     /**
12242      * @cfg {Boolean} removable (true|false) special filter default false
12243      */
12244     removable : false,
12245     
12246     /** @cfg {Boolean} grow @hide */
12247     /** @cfg {Number} growMin @hide */
12248     /** @cfg {Number} growMax @hide */
12249
12250     /**
12251      * @hide 
12252      * @method
12253      */
12254     autoSize: Roo.emptyFn,
12255     // private
12256     monitorTab : true,
12257     // private
12258     deferHeight : true,
12259
12260     
12261     actionMode : 'wrap',
12262     
12263     caret : false,
12264     
12265     
12266     getAutoCreate : function(){
12267        
12268         var align = this.labelAlign || this.parentLabelAlign();
12269         
12270         var id = Roo.id();
12271         
12272         var cfg = {
12273             cls: 'form-group' //input-group
12274         };
12275         
12276         
12277         var input =  {
12278             tag: 'input',
12279             id : id,
12280             type : this.inputType,
12281             cls : 'form-control',
12282             autocomplete: 'new-password',
12283             placeholder : this.placeholder || '' 
12284             
12285         };
12286         if (this.name) {
12287             input.name = this.name;
12288         }
12289         if (this.size) {
12290             input.cls += ' input-' + this.size;
12291         }
12292         
12293         if (this.disabled) {
12294             input.disabled=true;
12295         }
12296         
12297         var inputblock = input;
12298         
12299         if(this.hasFeedback && !this.allowBlank){
12300             
12301             var feedback = {
12302                 tag: 'span',
12303                 cls: 'glyphicon form-control-feedback'
12304             };
12305             
12306             if(this.removable && !this.editable  ){
12307                 inputblock = {
12308                     cls : 'has-feedback',
12309                     cn :  [
12310                         inputblock,
12311                         {
12312                             tag: 'button',
12313                             html : 'x',
12314                             cls : 'roo-combo-removable-btn close'
12315                         },
12316                         feedback
12317                     ] 
12318                 };
12319             } else {
12320                 inputblock = {
12321                     cls : 'has-feedback',
12322                     cn :  [
12323                         inputblock,
12324                         feedback
12325                     ] 
12326                 };
12327             }
12328
12329         } else {
12330             if(this.removable && !this.editable ){
12331                 inputblock = {
12332                     cls : 'roo-removable',
12333                     cn :  [
12334                         inputblock,
12335                         {
12336                             tag: 'button',
12337                             html : 'x',
12338                             cls : 'roo-combo-removable-btn close'
12339                         }
12340                     ] 
12341                 };
12342             }
12343         }
12344         
12345         if (this.before || this.after) {
12346             
12347             inputblock = {
12348                 cls : 'input-group',
12349                 cn :  [] 
12350             };
12351             if (this.before) {
12352                 inputblock.cn.push({
12353                     tag :'span',
12354                     cls : 'input-group-addon input-group-prepend input-group-text',
12355                     html : this.before
12356                 });
12357             }
12358             
12359             inputblock.cn.push(input);
12360             
12361             if(this.hasFeedback && !this.allowBlank){
12362                 inputblock.cls += ' has-feedback';
12363                 inputblock.cn.push(feedback);
12364             }
12365             
12366             if (this.after) {
12367                 inputblock.cn.push({
12368                     tag :'span',
12369                     cls : 'input-group-addon input-group-append input-group-text',
12370                     html : this.after
12371                 });
12372             }
12373             
12374         };
12375         
12376       
12377         
12378         var ibwrap = inputblock;
12379         
12380         if(this.multiple){
12381             ibwrap = {
12382                 tag: 'ul',
12383                 cls: 'roo-select2-choices',
12384                 cn:[
12385                     {
12386                         tag: 'li',
12387                         cls: 'roo-select2-search-field',
12388                         cn: [
12389
12390                             inputblock
12391                         ]
12392                     }
12393                 ]
12394             };
12395                 
12396         }
12397         
12398         var combobox = {
12399             cls: 'roo-select2-container input-group',
12400             cn: [
12401                  {
12402                     tag: 'input',
12403                     type : 'hidden',
12404                     cls: 'form-hidden-field'
12405                 },
12406                 ibwrap
12407             ]
12408         };
12409         
12410         if(!this.multiple && this.showToggleBtn){
12411             
12412             var caret = {
12413                         tag: 'span',
12414                         cls: 'caret'
12415              };
12416             if (this.caret != false) {
12417                 caret = {
12418                      tag: 'i',
12419                      cls: 'fa fa-' + this.caret
12420                 };
12421                 
12422             }
12423             
12424             combobox.cn.push({
12425                 tag :'span',
12426                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12427                 cn : [
12428                     Roo.bootstrap.version == 3 ? caret : '',
12429                     {
12430                         tag: 'span',
12431                         cls: 'combobox-clear',
12432                         cn  : [
12433                             {
12434                                 tag : 'i',
12435                                 cls: 'icon-remove'
12436                             }
12437                         ]
12438                     }
12439                 ]
12440
12441             })
12442         }
12443         
12444         if(this.multiple){
12445             combobox.cls += ' roo-select2-container-multi';
12446         }
12447          var indicator = {
12448             tag : 'i',
12449             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12450             tooltip : 'This field is required'
12451         };
12452         if (Roo.bootstrap.version == 4) {
12453             indicator = {
12454                 tag : 'i',
12455                 style : 'display:none'
12456             };
12457         }
12458         
12459         
12460         if (align ==='left' && this.fieldLabel.length) {
12461             
12462             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12463
12464             cfg.cn = [
12465                 indicator,
12466                 {
12467                     tag: 'label',
12468                     'for' :  id,
12469                     cls : 'control-label',
12470                     html : this.fieldLabel
12471
12472                 },
12473                 {
12474                     cls : "", 
12475                     cn: [
12476                         combobox
12477                     ]
12478                 }
12479
12480             ];
12481             
12482             var labelCfg = cfg.cn[1];
12483             var contentCfg = cfg.cn[2];
12484             
12485             if(this.indicatorpos == 'right'){
12486                 cfg.cn = [
12487                     {
12488                         tag: 'label',
12489                         'for' :  id,
12490                         cls : 'control-label',
12491                         cn : [
12492                             {
12493                                 tag : 'span',
12494                                 html : this.fieldLabel
12495                             },
12496                             indicator
12497                         ]
12498                     },
12499                     {
12500                         cls : "", 
12501                         cn: [
12502                             combobox
12503                         ]
12504                     }
12505
12506                 ];
12507                 
12508                 labelCfg = cfg.cn[0];
12509                 contentCfg = cfg.cn[1];
12510             }
12511             
12512             if(this.labelWidth > 12){
12513                 labelCfg.style = "width: " + this.labelWidth + 'px';
12514             }
12515             
12516             if(this.labelWidth < 13 && this.labelmd == 0){
12517                 this.labelmd = this.labelWidth;
12518             }
12519             
12520             if(this.labellg > 0){
12521                 labelCfg.cls += ' col-lg-' + this.labellg;
12522                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12523             }
12524             
12525             if(this.labelmd > 0){
12526                 labelCfg.cls += ' col-md-' + this.labelmd;
12527                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12528             }
12529             
12530             if(this.labelsm > 0){
12531                 labelCfg.cls += ' col-sm-' + this.labelsm;
12532                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12533             }
12534             
12535             if(this.labelxs > 0){
12536                 labelCfg.cls += ' col-xs-' + this.labelxs;
12537                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12538             }
12539             
12540         } else if ( this.fieldLabel.length) {
12541 //                Roo.log(" label");
12542             cfg.cn = [
12543                 indicator,
12544                {
12545                    tag: 'label',
12546                    //cls : 'input-group-addon',
12547                    html : this.fieldLabel
12548
12549                },
12550
12551                combobox
12552
12553             ];
12554             
12555             if(this.indicatorpos == 'right'){
12556                 
12557                 cfg.cn = [
12558                     {
12559                        tag: 'label',
12560                        cn : [
12561                            {
12562                                tag : 'span',
12563                                html : this.fieldLabel
12564                            },
12565                            indicator
12566                        ]
12567
12568                     },
12569                     combobox
12570
12571                 ];
12572
12573             }
12574
12575         } else {
12576             
12577 //                Roo.log(" no label && no align");
12578                 cfg = combobox
12579                      
12580                 
12581         }
12582         
12583         var settings=this;
12584         ['xs','sm','md','lg'].map(function(size){
12585             if (settings[size]) {
12586                 cfg.cls += ' col-' + size + '-' + settings[size];
12587             }
12588         });
12589         
12590         return cfg;
12591         
12592     },
12593     
12594     
12595     
12596     // private
12597     onResize : function(w, h){
12598 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12599 //        if(typeof w == 'number'){
12600 //            var x = w - this.trigger.getWidth();
12601 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12602 //            this.trigger.setStyle('left', x+'px');
12603 //        }
12604     },
12605
12606     // private
12607     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12608
12609     // private
12610     getResizeEl : function(){
12611         return this.inputEl();
12612     },
12613
12614     // private
12615     getPositionEl : function(){
12616         return this.inputEl();
12617     },
12618
12619     // private
12620     alignErrorIcon : function(){
12621         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12622     },
12623
12624     // private
12625     initEvents : function(){
12626         
12627         this.createList();
12628         
12629         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12630         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12631         if(!this.multiple && this.showToggleBtn){
12632             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12633             if(this.hideTrigger){
12634                 this.trigger.setDisplayed(false);
12635             }
12636             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12637         }
12638         
12639         if(this.multiple){
12640             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12641         }
12642         
12643         if(this.removable && !this.editable && !this.tickable){
12644             var close = this.closeTriggerEl();
12645             
12646             if(close){
12647                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12648                 close.on('click', this.removeBtnClick, this, close);
12649             }
12650         }
12651         
12652         //this.trigger.addClassOnOver('x-form-trigger-over');
12653         //this.trigger.addClassOnClick('x-form-trigger-click');
12654         
12655         //if(!this.width){
12656         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12657         //}
12658     },
12659     
12660     closeTriggerEl : function()
12661     {
12662         var close = this.el.select('.roo-combo-removable-btn', true).first();
12663         return close ? close : false;
12664     },
12665     
12666     removeBtnClick : function(e, h, el)
12667     {
12668         e.preventDefault();
12669         
12670         if(this.fireEvent("remove", this) !== false){
12671             this.reset();
12672             this.fireEvent("afterremove", this)
12673         }
12674     },
12675     
12676     createList : function()
12677     {
12678         this.list = Roo.get(document.body).createChild({
12679             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12680             cls: 'typeahead typeahead-long dropdown-menu shadow',
12681             style: 'display:none'
12682         });
12683         
12684         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12685         
12686     },
12687
12688     // private
12689     initTrigger : function(){
12690        
12691     },
12692
12693     // private
12694     onDestroy : function(){
12695         if(this.trigger){
12696             this.trigger.removeAllListeners();
12697           //  this.trigger.remove();
12698         }
12699         //if(this.wrap){
12700         //    this.wrap.remove();
12701         //}
12702         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12703     },
12704
12705     // private
12706     onFocus : function(){
12707         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12708         /*
12709         if(!this.mimicing){
12710             this.wrap.addClass('x-trigger-wrap-focus');
12711             this.mimicing = true;
12712             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12713             if(this.monitorTab){
12714                 this.el.on("keydown", this.checkTab, this);
12715             }
12716         }
12717         */
12718     },
12719
12720     // private
12721     checkTab : function(e){
12722         if(e.getKey() == e.TAB){
12723             this.triggerBlur();
12724         }
12725     },
12726
12727     // private
12728     onBlur : function(){
12729         // do nothing
12730     },
12731
12732     // private
12733     mimicBlur : function(e, t){
12734         /*
12735         if(!this.wrap.contains(t) && this.validateBlur()){
12736             this.triggerBlur();
12737         }
12738         */
12739     },
12740
12741     // private
12742     triggerBlur : function(){
12743         this.mimicing = false;
12744         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12745         if(this.monitorTab){
12746             this.el.un("keydown", this.checkTab, this);
12747         }
12748         //this.wrap.removeClass('x-trigger-wrap-focus');
12749         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12750     },
12751
12752     // private
12753     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12754     validateBlur : function(e, t){
12755         return true;
12756     },
12757
12758     // private
12759     onDisable : function(){
12760         this.inputEl().dom.disabled = true;
12761         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12762         //if(this.wrap){
12763         //    this.wrap.addClass('x-item-disabled');
12764         //}
12765     },
12766
12767     // private
12768     onEnable : function(){
12769         this.inputEl().dom.disabled = false;
12770         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12771         //if(this.wrap){
12772         //    this.el.removeClass('x-item-disabled');
12773         //}
12774     },
12775
12776     // private
12777     onShow : function(){
12778         var ae = this.getActionEl();
12779         
12780         if(ae){
12781             ae.dom.style.display = '';
12782             ae.dom.style.visibility = 'visible';
12783         }
12784     },
12785
12786     // private
12787     
12788     onHide : function(){
12789         var ae = this.getActionEl();
12790         ae.dom.style.display = 'none';
12791     },
12792
12793     /**
12794      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12795      * by an implementing function.
12796      * @method
12797      * @param {EventObject} e
12798      */
12799     onTriggerClick : Roo.emptyFn
12800 });
12801  
12802 /*
12803 * Licence: LGPL
12804 */
12805
12806 /**
12807  * @class Roo.bootstrap.CardUploader
12808  * @extends Roo.bootstrap.Button
12809  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12810  * @cfg {Number} errorTimeout default 3000
12811  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12812  * @cfg {Array}  html The button text.
12813
12814  *
12815  * @constructor
12816  * Create a new CardUploader
12817  * @param {Object} config The config object
12818  */
12819
12820 Roo.bootstrap.CardUploader = function(config){
12821     
12822  
12823     
12824     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12825     
12826     
12827     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12828         return r.data.id
12829      });
12830     
12831      this.addEvents({
12832          // raw events
12833         /**
12834          * @event preview
12835          * When a image is clicked on - and needs to display a slideshow or similar..
12836          * @param {Roo.bootstrap.Card} this
12837          * @param {Object} The image information data 
12838          *
12839          */
12840         'preview' : true,
12841          /**
12842          * @event download
12843          * When a the download link is clicked
12844          * @param {Roo.bootstrap.Card} this
12845          * @param {Object} The image information data  contains 
12846          */
12847         'download' : true
12848         
12849     });
12850 };
12851  
12852 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12853     
12854      
12855     errorTimeout : 3000,
12856      
12857     images : false,
12858    
12859     fileCollection : false,
12860     allowBlank : true,
12861     
12862     getAutoCreate : function()
12863     {
12864         
12865         var cfg =  {
12866             cls :'form-group' ,
12867             cn : [
12868                
12869                 {
12870                     tag: 'label',
12871                    //cls : 'input-group-addon',
12872                     html : this.fieldLabel
12873
12874                 },
12875
12876                 {
12877                     tag: 'input',
12878                     type : 'hidden',
12879                     name : this.name,
12880                     value : this.value,
12881                     cls : 'd-none  form-control'
12882                 },
12883                 
12884                 {
12885                     tag: 'input',
12886                     multiple : 'multiple',
12887                     type : 'file',
12888                     cls : 'd-none  roo-card-upload-selector'
12889                 },
12890                 
12891                 {
12892                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12893                 },
12894                 {
12895                     cls : 'card-columns roo-card-uploader-container'
12896                 }
12897
12898             ]
12899         };
12900            
12901          
12902         return cfg;
12903     },
12904     
12905     getChildContainer : function() /// what children are added to.
12906     {
12907         return this.containerEl;
12908     },
12909    
12910     getButtonContainer : function() /// what children are added to.
12911     {
12912         return this.el.select(".roo-card-uploader-button-container").first();
12913     },
12914    
12915     initEvents : function()
12916     {
12917         
12918         Roo.bootstrap.Input.prototype.initEvents.call(this);
12919         
12920         var t = this;
12921         this.addxtype({
12922             xns: Roo.bootstrap,
12923
12924             xtype : 'Button',
12925             container_method : 'getButtonContainer' ,            
12926             html :  this.html, // fix changable?
12927             cls : 'w-100 ',
12928             listeners : {
12929                 'click' : function(btn, e) {
12930                     t.onClick(e);
12931                 }
12932             }
12933         });
12934         
12935         
12936         
12937         
12938         this.urlAPI = (window.createObjectURL && window) || 
12939                                 (window.URL && URL.revokeObjectURL && URL) || 
12940                                 (window.webkitURL && webkitURL);
12941                         
12942          
12943          
12944          
12945         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12946         
12947         this.selectorEl.on('change', this.onFileSelected, this);
12948         if (this.images) {
12949             var t = this;
12950             this.images.forEach(function(img) {
12951                 t.addCard(img)
12952             });
12953             this.images = false;
12954         }
12955         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12956          
12957        
12958     },
12959     
12960    
12961     onClick : function(e)
12962     {
12963         e.preventDefault();
12964          
12965         this.selectorEl.dom.click();
12966          
12967     },
12968     
12969     onFileSelected : function(e)
12970     {
12971         e.preventDefault();
12972         
12973         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12974             return;
12975         }
12976         
12977         Roo.each(this.selectorEl.dom.files, function(file){    
12978             this.addFile(file);
12979         }, this);
12980          
12981     },
12982     
12983       
12984     
12985       
12986     
12987     addFile : function(file)
12988     {
12989            
12990         if(typeof(file) === 'string'){
12991             throw "Add file by name?"; // should not happen
12992             return;
12993         }
12994         
12995         if(!file || !this.urlAPI){
12996             return;
12997         }
12998         
12999         // file;
13000         // file.type;
13001         
13002         var _this = this;
13003         
13004         
13005         var url = _this.urlAPI.createObjectURL( file);
13006            
13007         this.addCard({
13008             id : Roo.bootstrap.CardUploader.ID--,
13009             is_uploaded : false,
13010             src : url,
13011             srcfile : file,
13012             title : file.name,
13013             mimetype : file.type,
13014             preview : false,
13015             is_deleted : 0
13016         });
13017         
13018     },
13019     
13020     /**
13021      * addCard - add an Attachment to the uploader
13022      * @param data - the data about the image to upload
13023      *
13024      * {
13025           id : 123
13026           title : "Title of file",
13027           is_uploaded : false,
13028           src : "http://.....",
13029           srcfile : { the File upload object },
13030           mimetype : file.type,
13031           preview : false,
13032           is_deleted : 0
13033           .. any other data...
13034         }
13035      *
13036      * 
13037     */
13038     
13039     addCard : function (data)
13040     {
13041         // hidden input element?
13042         // if the file is not an image...
13043         //then we need to use something other that and header_image
13044         var t = this;
13045         //   remove.....
13046         var footer = [
13047             {
13048                 xns : Roo.bootstrap,
13049                 xtype : 'CardFooter',
13050                  items: [
13051                     {
13052                         xns : Roo.bootstrap,
13053                         xtype : 'Element',
13054                         cls : 'd-flex',
13055                         items : [
13056                             
13057                             {
13058                                 xns : Roo.bootstrap,
13059                                 xtype : 'Button',
13060                                 html : String.format("<small>{0}</small>", data.title),
13061                                 cls : 'col-10 text-left',
13062                                 size: 'sm',
13063                                 weight: 'link',
13064                                 fa : 'download',
13065                                 listeners : {
13066                                     click : function() {
13067                                      
13068                                         t.fireEvent( "download", t, data );
13069                                     }
13070                                 }
13071                             },
13072                           
13073                             {
13074                                 xns : Roo.bootstrap,
13075                                 xtype : 'Button',
13076                                 style: 'max-height: 28px; ',
13077                                 size : 'sm',
13078                                 weight: 'danger',
13079                                 cls : 'col-2',
13080                                 fa : 'times',
13081                                 listeners : {
13082                                     click : function() {
13083                                         t.removeCard(data.id)
13084                                     }
13085                                 }
13086                             }
13087                         ]
13088                     }
13089                     
13090                 ] 
13091             }
13092             
13093         ];
13094         
13095         var cn = this.addxtype(
13096             {
13097                  
13098                 xns : Roo.bootstrap,
13099                 xtype : 'Card',
13100                 closeable : true,
13101                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13102                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13103                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13104                 data : data,
13105                 html : false,
13106                  
13107                 items : footer,
13108                 initEvents : function() {
13109                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13110                     var card = this;
13111                     this.imgEl = this.el.select('.card-img-top').first();
13112                     if (this.imgEl) {
13113                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13114                         this.imgEl.set({ 'pointer' : 'cursor' });
13115                                   
13116                     }
13117                     this.getCardFooter().addClass('p-1');
13118                     
13119                   
13120                 }
13121                 
13122             }
13123         );
13124         // dont' really need ot update items.
13125         // this.items.push(cn);
13126         this.fileCollection.add(cn);
13127         
13128         if (!data.srcfile) {
13129             this.updateInput();
13130             return;
13131         }
13132             
13133         var _t = this;
13134         var reader = new FileReader();
13135         reader.addEventListener("load", function() {  
13136             data.srcdata =  reader.result;
13137             _t.updateInput();
13138         });
13139         reader.readAsDataURL(data.srcfile);
13140         
13141         
13142         
13143     },
13144     removeCard : function(id)
13145     {
13146         
13147         var card  = this.fileCollection.get(id);
13148         card.data.is_deleted = 1;
13149         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13150         //this.fileCollection.remove(card);
13151         //this.items = this.items.filter(function(e) { return e != card });
13152         // dont' really need ot update items.
13153         card.el.dom.parentNode.removeChild(card.el.dom);
13154         this.updateInput();
13155
13156         
13157     },
13158     reset: function()
13159     {
13160         this.fileCollection.each(function(card) {
13161             if (card.el.dom && card.el.dom.parentNode) {
13162                 card.el.dom.parentNode.removeChild(card.el.dom);
13163             }
13164         });
13165         this.fileCollection.clear();
13166         this.updateInput();
13167     },
13168     
13169     updateInput : function()
13170     {
13171          var data = [];
13172         this.fileCollection.each(function(e) {
13173             data.push(e.data);
13174             
13175         });
13176         this.inputEl().dom.value = JSON.stringify(data);
13177         
13178         
13179         
13180     }
13181     
13182     
13183 });
13184
13185
13186 Roo.bootstrap.CardUploader.ID = -1;/*
13187  * Based on:
13188  * Ext JS Library 1.1.1
13189  * Copyright(c) 2006-2007, Ext JS, LLC.
13190  *
13191  * Originally Released Under LGPL - original licence link has changed is not relivant.
13192  *
13193  * Fork - LGPL
13194  * <script type="text/javascript">
13195  */
13196
13197
13198 /**
13199  * @class Roo.data.SortTypes
13200  * @singleton
13201  * Defines the default sorting (casting?) comparison functions used when sorting data.
13202  */
13203 Roo.data.SortTypes = {
13204     /**
13205      * Default sort that does nothing
13206      * @param {Mixed} s The value being converted
13207      * @return {Mixed} The comparison value
13208      */
13209     none : function(s){
13210         return s;
13211     },
13212     
13213     /**
13214      * The regular expression used to strip tags
13215      * @type {RegExp}
13216      * @property
13217      */
13218     stripTagsRE : /<\/?[^>]+>/gi,
13219     
13220     /**
13221      * Strips all HTML tags to sort on text only
13222      * @param {Mixed} s The value being converted
13223      * @return {String} The comparison value
13224      */
13225     asText : function(s){
13226         return String(s).replace(this.stripTagsRE, "");
13227     },
13228     
13229     /**
13230      * Strips all HTML tags to sort on text only - Case insensitive
13231      * @param {Mixed} s The value being converted
13232      * @return {String} The comparison value
13233      */
13234     asUCText : function(s){
13235         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13236     },
13237     
13238     /**
13239      * Case insensitive string
13240      * @param {Mixed} s The value being converted
13241      * @return {String} The comparison value
13242      */
13243     asUCString : function(s) {
13244         return String(s).toUpperCase();
13245     },
13246     
13247     /**
13248      * Date sorting
13249      * @param {Mixed} s The value being converted
13250      * @return {Number} The comparison value
13251      */
13252     asDate : function(s) {
13253         if(!s){
13254             return 0;
13255         }
13256         if(s instanceof Date){
13257             return s.getTime();
13258         }
13259         return Date.parse(String(s));
13260     },
13261     
13262     /**
13263      * Float sorting
13264      * @param {Mixed} s The value being converted
13265      * @return {Float} The comparison value
13266      */
13267     asFloat : function(s) {
13268         var val = parseFloat(String(s).replace(/,/g, ""));
13269         if(isNaN(val)) {
13270             val = 0;
13271         }
13272         return val;
13273     },
13274     
13275     /**
13276      * Integer sorting
13277      * @param {Mixed} s The value being converted
13278      * @return {Number} The comparison value
13279      */
13280     asInt : function(s) {
13281         var val = parseInt(String(s).replace(/,/g, ""));
13282         if(isNaN(val)) {
13283             val = 0;
13284         }
13285         return val;
13286     }
13287 };/*
13288  * Based on:
13289  * Ext JS Library 1.1.1
13290  * Copyright(c) 2006-2007, Ext JS, LLC.
13291  *
13292  * Originally Released Under LGPL - original licence link has changed is not relivant.
13293  *
13294  * Fork - LGPL
13295  * <script type="text/javascript">
13296  */
13297
13298 /**
13299 * @class Roo.data.Record
13300  * Instances of this class encapsulate both record <em>definition</em> information, and record
13301  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13302  * to access Records cached in an {@link Roo.data.Store} object.<br>
13303  * <p>
13304  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13305  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13306  * objects.<br>
13307  * <p>
13308  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13309  * @constructor
13310  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13311  * {@link #create}. The parameters are the same.
13312  * @param {Array} data An associative Array of data values keyed by the field name.
13313  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13314  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13315  * not specified an integer id is generated.
13316  */
13317 Roo.data.Record = function(data, id){
13318     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13319     this.data = data;
13320 };
13321
13322 /**
13323  * Generate a constructor for a specific record layout.
13324  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13325  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13326  * Each field definition object may contain the following properties: <ul>
13327  * <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,
13328  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13329  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13330  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13331  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13332  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13333  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13334  * this may be omitted.</p></li>
13335  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13336  * <ul><li>auto (Default, implies no conversion)</li>
13337  * <li>string</li>
13338  * <li>int</li>
13339  * <li>float</li>
13340  * <li>boolean</li>
13341  * <li>date</li></ul></p></li>
13342  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13343  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13344  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13345  * by the Reader into an object that will be stored in the Record. It is passed the
13346  * following parameters:<ul>
13347  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13348  * </ul></p></li>
13349  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13350  * </ul>
13351  * <br>usage:<br><pre><code>
13352 var TopicRecord = Roo.data.Record.create(
13353     {name: 'title', mapping: 'topic_title'},
13354     {name: 'author', mapping: 'username'},
13355     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13356     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13357     {name: 'lastPoster', mapping: 'user2'},
13358     {name: 'excerpt', mapping: 'post_text'}
13359 );
13360
13361 var myNewRecord = new TopicRecord({
13362     title: 'Do my job please',
13363     author: 'noobie',
13364     totalPosts: 1,
13365     lastPost: new Date(),
13366     lastPoster: 'Animal',
13367     excerpt: 'No way dude!'
13368 });
13369 myStore.add(myNewRecord);
13370 </code></pre>
13371  * @method create
13372  * @static
13373  */
13374 Roo.data.Record.create = function(o){
13375     var f = function(){
13376         f.superclass.constructor.apply(this, arguments);
13377     };
13378     Roo.extend(f, Roo.data.Record);
13379     var p = f.prototype;
13380     p.fields = new Roo.util.MixedCollection(false, function(field){
13381         return field.name;
13382     });
13383     for(var i = 0, len = o.length; i < len; i++){
13384         p.fields.add(new Roo.data.Field(o[i]));
13385     }
13386     f.getField = function(name){
13387         return p.fields.get(name);  
13388     };
13389     return f;
13390 };
13391
13392 Roo.data.Record.AUTO_ID = 1000;
13393 Roo.data.Record.EDIT = 'edit';
13394 Roo.data.Record.REJECT = 'reject';
13395 Roo.data.Record.COMMIT = 'commit';
13396
13397 Roo.data.Record.prototype = {
13398     /**
13399      * Readonly flag - true if this record has been modified.
13400      * @type Boolean
13401      */
13402     dirty : false,
13403     editing : false,
13404     error: null,
13405     modified: null,
13406
13407     // private
13408     join : function(store){
13409         this.store = store;
13410     },
13411
13412     /**
13413      * Set the named field to the specified value.
13414      * @param {String} name The name of the field to set.
13415      * @param {Object} value The value to set the field to.
13416      */
13417     set : function(name, value){
13418         if(this.data[name] == value){
13419             return;
13420         }
13421         this.dirty = true;
13422         if(!this.modified){
13423             this.modified = {};
13424         }
13425         if(typeof this.modified[name] == 'undefined'){
13426             this.modified[name] = this.data[name];
13427         }
13428         this.data[name] = value;
13429         if(!this.editing && this.store){
13430             this.store.afterEdit(this);
13431         }       
13432     },
13433
13434     /**
13435      * Get the value of the named field.
13436      * @param {String} name The name of the field to get the value of.
13437      * @return {Object} The value of the field.
13438      */
13439     get : function(name){
13440         return this.data[name]; 
13441     },
13442
13443     // private
13444     beginEdit : function(){
13445         this.editing = true;
13446         this.modified = {}; 
13447     },
13448
13449     // private
13450     cancelEdit : function(){
13451         this.editing = false;
13452         delete this.modified;
13453     },
13454
13455     // private
13456     endEdit : function(){
13457         this.editing = false;
13458         if(this.dirty && this.store){
13459             this.store.afterEdit(this);
13460         }
13461     },
13462
13463     /**
13464      * Usually called by the {@link Roo.data.Store} which owns the Record.
13465      * Rejects all changes made to the Record since either creation, or the last commit operation.
13466      * Modified fields are reverted to their original values.
13467      * <p>
13468      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13469      * of reject operations.
13470      */
13471     reject : function(){
13472         var m = this.modified;
13473         for(var n in m){
13474             if(typeof m[n] != "function"){
13475                 this.data[n] = m[n];
13476             }
13477         }
13478         this.dirty = false;
13479         delete this.modified;
13480         this.editing = false;
13481         if(this.store){
13482             this.store.afterReject(this);
13483         }
13484     },
13485
13486     /**
13487      * Usually called by the {@link Roo.data.Store} which owns the Record.
13488      * Commits all changes made to the Record since either creation, or the last commit operation.
13489      * <p>
13490      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13491      * of commit operations.
13492      */
13493     commit : function(){
13494         this.dirty = false;
13495         delete this.modified;
13496         this.editing = false;
13497         if(this.store){
13498             this.store.afterCommit(this);
13499         }
13500     },
13501
13502     // private
13503     hasError : function(){
13504         return this.error != null;
13505     },
13506
13507     // private
13508     clearError : function(){
13509         this.error = null;
13510     },
13511
13512     /**
13513      * Creates a copy of this record.
13514      * @param {String} id (optional) A new record id if you don't want to use this record's id
13515      * @return {Record}
13516      */
13517     copy : function(newId) {
13518         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13519     }
13520 };/*
13521  * Based on:
13522  * Ext JS Library 1.1.1
13523  * Copyright(c) 2006-2007, Ext JS, LLC.
13524  *
13525  * Originally Released Under LGPL - original licence link has changed is not relivant.
13526  *
13527  * Fork - LGPL
13528  * <script type="text/javascript">
13529  */
13530
13531
13532
13533 /**
13534  * @class Roo.data.Store
13535  * @extends Roo.util.Observable
13536  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13537  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13538  * <p>
13539  * 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
13540  * has no knowledge of the format of the data returned by the Proxy.<br>
13541  * <p>
13542  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13543  * instances from the data object. These records are cached and made available through accessor functions.
13544  * @constructor
13545  * Creates a new Store.
13546  * @param {Object} config A config object containing the objects needed for the Store to access data,
13547  * and read the data into Records.
13548  */
13549 Roo.data.Store = function(config){
13550     this.data = new Roo.util.MixedCollection(false);
13551     this.data.getKey = function(o){
13552         return o.id;
13553     };
13554     this.baseParams = {};
13555     // private
13556     this.paramNames = {
13557         "start" : "start",
13558         "limit" : "limit",
13559         "sort" : "sort",
13560         "dir" : "dir",
13561         "multisort" : "_multisort"
13562     };
13563
13564     if(config && config.data){
13565         this.inlineData = config.data;
13566         delete config.data;
13567     }
13568
13569     Roo.apply(this, config);
13570     
13571     if(this.reader){ // reader passed
13572         this.reader = Roo.factory(this.reader, Roo.data);
13573         this.reader.xmodule = this.xmodule || false;
13574         if(!this.recordType){
13575             this.recordType = this.reader.recordType;
13576         }
13577         if(this.reader.onMetaChange){
13578             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13579         }
13580     }
13581
13582     if(this.recordType){
13583         this.fields = this.recordType.prototype.fields;
13584     }
13585     this.modified = [];
13586
13587     this.addEvents({
13588         /**
13589          * @event datachanged
13590          * Fires when the data cache has changed, and a widget which is using this Store
13591          * as a Record cache should refresh its view.
13592          * @param {Store} this
13593          */
13594         datachanged : true,
13595         /**
13596          * @event metachange
13597          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13598          * @param {Store} this
13599          * @param {Object} meta The JSON metadata
13600          */
13601         metachange : true,
13602         /**
13603          * @event add
13604          * Fires when Records have been added to the Store
13605          * @param {Store} this
13606          * @param {Roo.data.Record[]} records The array of Records added
13607          * @param {Number} index The index at which the record(s) were added
13608          */
13609         add : true,
13610         /**
13611          * @event remove
13612          * Fires when a Record has been removed from the Store
13613          * @param {Store} this
13614          * @param {Roo.data.Record} record The Record that was removed
13615          * @param {Number} index The index at which the record was removed
13616          */
13617         remove : true,
13618         /**
13619          * @event update
13620          * Fires when a Record has been updated
13621          * @param {Store} this
13622          * @param {Roo.data.Record} record The Record that was updated
13623          * @param {String} operation The update operation being performed.  Value may be one of:
13624          * <pre><code>
13625  Roo.data.Record.EDIT
13626  Roo.data.Record.REJECT
13627  Roo.data.Record.COMMIT
13628          * </code></pre>
13629          */
13630         update : true,
13631         /**
13632          * @event clear
13633          * Fires when the data cache has been cleared.
13634          * @param {Store} this
13635          */
13636         clear : true,
13637         /**
13638          * @event beforeload
13639          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13640          * the load action will be canceled.
13641          * @param {Store} this
13642          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13643          */
13644         beforeload : true,
13645         /**
13646          * @event beforeloadadd
13647          * Fires after a new set of Records has been loaded.
13648          * @param {Store} this
13649          * @param {Roo.data.Record[]} records The Records that were loaded
13650          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13651          */
13652         beforeloadadd : true,
13653         /**
13654          * @event load
13655          * Fires after a new set of Records has been loaded, before they are added to the store.
13656          * @param {Store} this
13657          * @param {Roo.data.Record[]} records The Records that were loaded
13658          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13659          * @params {Object} return from reader
13660          */
13661         load : true,
13662         /**
13663          * @event loadexception
13664          * Fires if an exception occurs in the Proxy during loading.
13665          * Called with the signature of the Proxy's "loadexception" event.
13666          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13667          * 
13668          * @param {Proxy} 
13669          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13670          * @param {Object} load options 
13671          * @param {Object} jsonData from your request (normally this contains the Exception)
13672          */
13673         loadexception : true
13674     });
13675     
13676     if(this.proxy){
13677         this.proxy = Roo.factory(this.proxy, Roo.data);
13678         this.proxy.xmodule = this.xmodule || false;
13679         this.relayEvents(this.proxy,  ["loadexception"]);
13680     }
13681     this.sortToggle = {};
13682     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13683
13684     Roo.data.Store.superclass.constructor.call(this);
13685
13686     if(this.inlineData){
13687         this.loadData(this.inlineData);
13688         delete this.inlineData;
13689     }
13690 };
13691
13692 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13693      /**
13694     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13695     * without a remote query - used by combo/forms at present.
13696     */
13697     
13698     /**
13699     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13700     */
13701     /**
13702     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13703     */
13704     /**
13705     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13706     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13707     */
13708     /**
13709     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13710     * on any HTTP request
13711     */
13712     /**
13713     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13714     */
13715     /**
13716     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13717     */
13718     multiSort: false,
13719     /**
13720     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13721     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13722     */
13723     remoteSort : false,
13724
13725     /**
13726     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13727      * loaded or when a record is removed. (defaults to false).
13728     */
13729     pruneModifiedRecords : false,
13730
13731     // private
13732     lastOptions : null,
13733
13734     /**
13735      * Add Records to the Store and fires the add event.
13736      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13737      */
13738     add : function(records){
13739         records = [].concat(records);
13740         for(var i = 0, len = records.length; i < len; i++){
13741             records[i].join(this);
13742         }
13743         var index = this.data.length;
13744         this.data.addAll(records);
13745         this.fireEvent("add", this, records, index);
13746     },
13747
13748     /**
13749      * Remove a Record from the Store and fires the remove event.
13750      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13751      */
13752     remove : function(record){
13753         var index = this.data.indexOf(record);
13754         this.data.removeAt(index);
13755  
13756         if(this.pruneModifiedRecords){
13757             this.modified.remove(record);
13758         }
13759         this.fireEvent("remove", this, record, index);
13760     },
13761
13762     /**
13763      * Remove all Records from the Store and fires the clear event.
13764      */
13765     removeAll : function(){
13766         this.data.clear();
13767         if(this.pruneModifiedRecords){
13768             this.modified = [];
13769         }
13770         this.fireEvent("clear", this);
13771     },
13772
13773     /**
13774      * Inserts Records to the Store at the given index and fires the add event.
13775      * @param {Number} index The start index at which to insert the passed Records.
13776      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13777      */
13778     insert : function(index, records){
13779         records = [].concat(records);
13780         for(var i = 0, len = records.length; i < len; i++){
13781             this.data.insert(index, records[i]);
13782             records[i].join(this);
13783         }
13784         this.fireEvent("add", this, records, index);
13785     },
13786
13787     /**
13788      * Get the index within the cache of the passed Record.
13789      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13790      * @return {Number} The index of the passed Record. Returns -1 if not found.
13791      */
13792     indexOf : function(record){
13793         return this.data.indexOf(record);
13794     },
13795
13796     /**
13797      * Get the index within the cache of the Record with the passed id.
13798      * @param {String} id The id of the Record to find.
13799      * @return {Number} The index of the Record. Returns -1 if not found.
13800      */
13801     indexOfId : function(id){
13802         return this.data.indexOfKey(id);
13803     },
13804
13805     /**
13806      * Get the Record with the specified id.
13807      * @param {String} id The id of the Record to find.
13808      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13809      */
13810     getById : function(id){
13811         return this.data.key(id);
13812     },
13813
13814     /**
13815      * Get the Record at the specified index.
13816      * @param {Number} index The index of the Record to find.
13817      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13818      */
13819     getAt : function(index){
13820         return this.data.itemAt(index);
13821     },
13822
13823     /**
13824      * Returns a range of Records between specified indices.
13825      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13826      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13827      * @return {Roo.data.Record[]} An array of Records
13828      */
13829     getRange : function(start, end){
13830         return this.data.getRange(start, end);
13831     },
13832
13833     // private
13834     storeOptions : function(o){
13835         o = Roo.apply({}, o);
13836         delete o.callback;
13837         delete o.scope;
13838         this.lastOptions = o;
13839     },
13840
13841     /**
13842      * Loads the Record cache from the configured Proxy using the configured Reader.
13843      * <p>
13844      * If using remote paging, then the first load call must specify the <em>start</em>
13845      * and <em>limit</em> properties in the options.params property to establish the initial
13846      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13847      * <p>
13848      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13849      * and this call will return before the new data has been loaded. Perform any post-processing
13850      * in a callback function, or in a "load" event handler.</strong>
13851      * <p>
13852      * @param {Object} options An object containing properties which control loading options:<ul>
13853      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13854      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13855      * passed the following arguments:<ul>
13856      * <li>r : Roo.data.Record[]</li>
13857      * <li>options: Options object from the load call</li>
13858      * <li>success: Boolean success indicator</li></ul></li>
13859      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13860      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13861      * </ul>
13862      */
13863     load : function(options){
13864         options = options || {};
13865         if(this.fireEvent("beforeload", this, options) !== false){
13866             this.storeOptions(options);
13867             var p = Roo.apply(options.params || {}, this.baseParams);
13868             // if meta was not loaded from remote source.. try requesting it.
13869             if (!this.reader.metaFromRemote) {
13870                 p._requestMeta = 1;
13871             }
13872             if(this.sortInfo && this.remoteSort){
13873                 var pn = this.paramNames;
13874                 p[pn["sort"]] = this.sortInfo.field;
13875                 p[pn["dir"]] = this.sortInfo.direction;
13876             }
13877             if (this.multiSort) {
13878                 var pn = this.paramNames;
13879                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13880             }
13881             
13882             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13883         }
13884     },
13885
13886     /**
13887      * Reloads the Record cache from the configured Proxy using the configured Reader and
13888      * the options from the last load operation performed.
13889      * @param {Object} options (optional) An object containing properties which may override the options
13890      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13891      * the most recently used options are reused).
13892      */
13893     reload : function(options){
13894         this.load(Roo.applyIf(options||{}, this.lastOptions));
13895     },
13896
13897     // private
13898     // Called as a callback by the Reader during a load operation.
13899     loadRecords : function(o, options, success){
13900         if(!o || success === false){
13901             if(success !== false){
13902                 this.fireEvent("load", this, [], options, o);
13903             }
13904             if(options.callback){
13905                 options.callback.call(options.scope || this, [], options, false);
13906             }
13907             return;
13908         }
13909         // if data returned failure - throw an exception.
13910         if (o.success === false) {
13911             // show a message if no listener is registered.
13912             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13913                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13914             }
13915             // loadmask wil be hooked into this..
13916             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13917             return;
13918         }
13919         var r = o.records, t = o.totalRecords || r.length;
13920         
13921         this.fireEvent("beforeloadadd", this, r, options, o);
13922         
13923         if(!options || options.add !== true){
13924             if(this.pruneModifiedRecords){
13925                 this.modified = [];
13926             }
13927             for(var i = 0, len = r.length; i < len; i++){
13928                 r[i].join(this);
13929             }
13930             if(this.snapshot){
13931                 this.data = this.snapshot;
13932                 delete this.snapshot;
13933             }
13934             this.data.clear();
13935             this.data.addAll(r);
13936             this.totalLength = t;
13937             this.applySort();
13938             this.fireEvent("datachanged", this);
13939         }else{
13940             this.totalLength = Math.max(t, this.data.length+r.length);
13941             this.add(r);
13942         }
13943         
13944         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13945                 
13946             var e = new Roo.data.Record({});
13947
13948             e.set(this.parent.displayField, this.parent.emptyTitle);
13949             e.set(this.parent.valueField, '');
13950
13951             this.insert(0, e);
13952         }
13953             
13954         this.fireEvent("load", this, r, options, o);
13955         if(options.callback){
13956             options.callback.call(options.scope || this, r, options, true);
13957         }
13958     },
13959
13960
13961     /**
13962      * Loads data from a passed data block. A Reader which understands the format of the data
13963      * must have been configured in the constructor.
13964      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13965      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13966      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13967      */
13968     loadData : function(o, append){
13969         var r = this.reader.readRecords(o);
13970         this.loadRecords(r, {add: append}, true);
13971     },
13972     
13973      /**
13974      * using 'cn' the nested child reader read the child array into it's child stores.
13975      * @param {Object} rec The record with a 'children array
13976      */
13977     loadDataFromChildren : function(rec)
13978     {
13979         this.loadData(this.reader.toLoadData(rec));
13980     },
13981     
13982
13983     /**
13984      * Gets the number of cached records.
13985      * <p>
13986      * <em>If using paging, this may not be the total size of the dataset. If the data object
13987      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13988      * the data set size</em>
13989      */
13990     getCount : function(){
13991         return this.data.length || 0;
13992     },
13993
13994     /**
13995      * Gets the total number of records in the dataset as returned by the server.
13996      * <p>
13997      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13998      * the dataset size</em>
13999      */
14000     getTotalCount : function(){
14001         return this.totalLength || 0;
14002     },
14003
14004     /**
14005      * Returns the sort state of the Store as an object with two properties:
14006      * <pre><code>
14007  field {String} The name of the field by which the Records are sorted
14008  direction {String} The sort order, "ASC" or "DESC"
14009      * </code></pre>
14010      */
14011     getSortState : function(){
14012         return this.sortInfo;
14013     },
14014
14015     // private
14016     applySort : function(){
14017         if(this.sortInfo && !this.remoteSort){
14018             var s = this.sortInfo, f = s.field;
14019             var st = this.fields.get(f).sortType;
14020             var fn = function(r1, r2){
14021                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14022                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14023             };
14024             this.data.sort(s.direction, fn);
14025             if(this.snapshot && this.snapshot != this.data){
14026                 this.snapshot.sort(s.direction, fn);
14027             }
14028         }
14029     },
14030
14031     /**
14032      * Sets the default sort column and order to be used by the next load operation.
14033      * @param {String} fieldName The name of the field to sort by.
14034      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14035      */
14036     setDefaultSort : function(field, dir){
14037         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14038     },
14039
14040     /**
14041      * Sort the Records.
14042      * If remote sorting is used, the sort is performed on the server, and the cache is
14043      * reloaded. If local sorting is used, the cache is sorted internally.
14044      * @param {String} fieldName The name of the field to sort by.
14045      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14046      */
14047     sort : function(fieldName, dir){
14048         var f = this.fields.get(fieldName);
14049         if(!dir){
14050             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14051             
14052             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14053                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14054             }else{
14055                 dir = f.sortDir;
14056             }
14057         }
14058         this.sortToggle[f.name] = dir;
14059         this.sortInfo = {field: f.name, direction: dir};
14060         if(!this.remoteSort){
14061             this.applySort();
14062             this.fireEvent("datachanged", this);
14063         }else{
14064             this.load(this.lastOptions);
14065         }
14066     },
14067
14068     /**
14069      * Calls the specified function for each of the Records in the cache.
14070      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14071      * Returning <em>false</em> aborts and exits the iteration.
14072      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14073      */
14074     each : function(fn, scope){
14075         this.data.each(fn, scope);
14076     },
14077
14078     /**
14079      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14080      * (e.g., during paging).
14081      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14082      */
14083     getModifiedRecords : function(){
14084         return this.modified;
14085     },
14086
14087     // private
14088     createFilterFn : function(property, value, anyMatch){
14089         if(!value.exec){ // not a regex
14090             value = String(value);
14091             if(value.length == 0){
14092                 return false;
14093             }
14094             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14095         }
14096         return function(r){
14097             return value.test(r.data[property]);
14098         };
14099     },
14100
14101     /**
14102      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14103      * @param {String} property A field on your records
14104      * @param {Number} start The record index to start at (defaults to 0)
14105      * @param {Number} end The last record index to include (defaults to length - 1)
14106      * @return {Number} The sum
14107      */
14108     sum : function(property, start, end){
14109         var rs = this.data.items, v = 0;
14110         start = start || 0;
14111         end = (end || end === 0) ? end : rs.length-1;
14112
14113         for(var i = start; i <= end; i++){
14114             v += (rs[i].data[property] || 0);
14115         }
14116         return v;
14117     },
14118
14119     /**
14120      * Filter the records by a specified property.
14121      * @param {String} field A field on your records
14122      * @param {String/RegExp} value Either a string that the field
14123      * should start with or a RegExp to test against the field
14124      * @param {Boolean} anyMatch True to match any part not just the beginning
14125      */
14126     filter : function(property, value, anyMatch){
14127         var fn = this.createFilterFn(property, value, anyMatch);
14128         return fn ? this.filterBy(fn) : this.clearFilter();
14129     },
14130
14131     /**
14132      * Filter by a function. The specified function will be called with each
14133      * record in this data source. If the function returns true the record is included,
14134      * otherwise it is filtered.
14135      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14136      * @param {Object} scope (optional) The scope of the function (defaults to this)
14137      */
14138     filterBy : function(fn, scope){
14139         this.snapshot = this.snapshot || this.data;
14140         this.data = this.queryBy(fn, scope||this);
14141         this.fireEvent("datachanged", this);
14142     },
14143
14144     /**
14145      * Query the records by a specified property.
14146      * @param {String} field A field on your records
14147      * @param {String/RegExp} value Either a string that the field
14148      * should start with or a RegExp to test against the field
14149      * @param {Boolean} anyMatch True to match any part not just the beginning
14150      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14151      */
14152     query : function(property, value, anyMatch){
14153         var fn = this.createFilterFn(property, value, anyMatch);
14154         return fn ? this.queryBy(fn) : this.data.clone();
14155     },
14156
14157     /**
14158      * Query by a function. The specified function will be called with each
14159      * record in this data source. If the function returns true the record is included
14160      * in the results.
14161      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14162      * @param {Object} scope (optional) The scope of the function (defaults to this)
14163       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14164      **/
14165     queryBy : function(fn, scope){
14166         var data = this.snapshot || this.data;
14167         return data.filterBy(fn, scope||this);
14168     },
14169
14170     /**
14171      * Collects unique values for a particular dataIndex from this store.
14172      * @param {String} dataIndex The property to collect
14173      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14174      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14175      * @return {Array} An array of the unique values
14176      **/
14177     collect : function(dataIndex, allowNull, bypassFilter){
14178         var d = (bypassFilter === true && this.snapshot) ?
14179                 this.snapshot.items : this.data.items;
14180         var v, sv, r = [], l = {};
14181         for(var i = 0, len = d.length; i < len; i++){
14182             v = d[i].data[dataIndex];
14183             sv = String(v);
14184             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14185                 l[sv] = true;
14186                 r[r.length] = v;
14187             }
14188         }
14189         return r;
14190     },
14191
14192     /**
14193      * Revert to a view of the Record cache with no filtering applied.
14194      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14195      */
14196     clearFilter : function(suppressEvent){
14197         if(this.snapshot && this.snapshot != this.data){
14198             this.data = this.snapshot;
14199             delete this.snapshot;
14200             if(suppressEvent !== true){
14201                 this.fireEvent("datachanged", this);
14202             }
14203         }
14204     },
14205
14206     // private
14207     afterEdit : function(record){
14208         if(this.modified.indexOf(record) == -1){
14209             this.modified.push(record);
14210         }
14211         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14212     },
14213     
14214     // private
14215     afterReject : function(record){
14216         this.modified.remove(record);
14217         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14218     },
14219
14220     // private
14221     afterCommit : function(record){
14222         this.modified.remove(record);
14223         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14224     },
14225
14226     /**
14227      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14228      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14229      */
14230     commitChanges : function(){
14231         var m = this.modified.slice(0);
14232         this.modified = [];
14233         for(var i = 0, len = m.length; i < len; i++){
14234             m[i].commit();
14235         }
14236     },
14237
14238     /**
14239      * Cancel outstanding changes on all changed records.
14240      */
14241     rejectChanges : function(){
14242         var m = this.modified.slice(0);
14243         this.modified = [];
14244         for(var i = 0, len = m.length; i < len; i++){
14245             m[i].reject();
14246         }
14247     },
14248
14249     onMetaChange : function(meta, rtype, o){
14250         this.recordType = rtype;
14251         this.fields = rtype.prototype.fields;
14252         delete this.snapshot;
14253         this.sortInfo = meta.sortInfo || this.sortInfo;
14254         this.modified = [];
14255         this.fireEvent('metachange', this, this.reader.meta);
14256     },
14257     
14258     moveIndex : function(data, type)
14259     {
14260         var index = this.indexOf(data);
14261         
14262         var newIndex = index + type;
14263         
14264         this.remove(data);
14265         
14266         this.insert(newIndex, data);
14267         
14268     }
14269 });/*
14270  * Based on:
14271  * Ext JS Library 1.1.1
14272  * Copyright(c) 2006-2007, Ext JS, LLC.
14273  *
14274  * Originally Released Under LGPL - original licence link has changed is not relivant.
14275  *
14276  * Fork - LGPL
14277  * <script type="text/javascript">
14278  */
14279
14280 /**
14281  * @class Roo.data.SimpleStore
14282  * @extends Roo.data.Store
14283  * Small helper class to make creating Stores from Array data easier.
14284  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14285  * @cfg {Array} fields An array of field definition objects, or field name strings.
14286  * @cfg {Object} an existing reader (eg. copied from another store)
14287  * @cfg {Array} data The multi-dimensional array of data
14288  * @constructor
14289  * @param {Object} config
14290  */
14291 Roo.data.SimpleStore = function(config)
14292 {
14293     Roo.data.SimpleStore.superclass.constructor.call(this, {
14294         isLocal : true,
14295         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14296                 id: config.id
14297             },
14298             Roo.data.Record.create(config.fields)
14299         ),
14300         proxy : new Roo.data.MemoryProxy(config.data)
14301     });
14302     this.load();
14303 };
14304 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314
14315 /**
14316 /**
14317  * @extends Roo.data.Store
14318  * @class Roo.data.JsonStore
14319  * Small helper class to make creating Stores for JSON data easier. <br/>
14320 <pre><code>
14321 var store = new Roo.data.JsonStore({
14322     url: 'get-images.php',
14323     root: 'images',
14324     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14325 });
14326 </code></pre>
14327  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14328  * JsonReader and HttpProxy (unless inline data is provided).</b>
14329  * @cfg {Array} fields An array of field definition objects, or field name strings.
14330  * @constructor
14331  * @param {Object} config
14332  */
14333 Roo.data.JsonStore = function(c){
14334     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14335         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14336         reader: new Roo.data.JsonReader(c, c.fields)
14337     }));
14338 };
14339 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14340  * Based on:
14341  * Ext JS Library 1.1.1
14342  * Copyright(c) 2006-2007, Ext JS, LLC.
14343  *
14344  * Originally Released Under LGPL - original licence link has changed is not relivant.
14345  *
14346  * Fork - LGPL
14347  * <script type="text/javascript">
14348  */
14349
14350  
14351 Roo.data.Field = function(config){
14352     if(typeof config == "string"){
14353         config = {name: config};
14354     }
14355     Roo.apply(this, config);
14356     
14357     if(!this.type){
14358         this.type = "auto";
14359     }
14360     
14361     var st = Roo.data.SortTypes;
14362     // named sortTypes are supported, here we look them up
14363     if(typeof this.sortType == "string"){
14364         this.sortType = st[this.sortType];
14365     }
14366     
14367     // set default sortType for strings and dates
14368     if(!this.sortType){
14369         switch(this.type){
14370             case "string":
14371                 this.sortType = st.asUCString;
14372                 break;
14373             case "date":
14374                 this.sortType = st.asDate;
14375                 break;
14376             default:
14377                 this.sortType = st.none;
14378         }
14379     }
14380
14381     // define once
14382     var stripRe = /[\$,%]/g;
14383
14384     // prebuilt conversion function for this field, instead of
14385     // switching every time we're reading a value
14386     if(!this.convert){
14387         var cv, dateFormat = this.dateFormat;
14388         switch(this.type){
14389             case "":
14390             case "auto":
14391             case undefined:
14392                 cv = function(v){ return v; };
14393                 break;
14394             case "string":
14395                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14396                 break;
14397             case "int":
14398                 cv = function(v){
14399                     return v !== undefined && v !== null && v !== '' ?
14400                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14401                     };
14402                 break;
14403             case "float":
14404                 cv = function(v){
14405                     return v !== undefined && v !== null && v !== '' ?
14406                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14407                     };
14408                 break;
14409             case "bool":
14410             case "boolean":
14411                 cv = function(v){ return v === true || v === "true" || v == 1; };
14412                 break;
14413             case "date":
14414                 cv = function(v){
14415                     if(!v){
14416                         return '';
14417                     }
14418                     if(v instanceof Date){
14419                         return v;
14420                     }
14421                     if(dateFormat){
14422                         if(dateFormat == "timestamp"){
14423                             return new Date(v*1000);
14424                         }
14425                         return Date.parseDate(v, dateFormat);
14426                     }
14427                     var parsed = Date.parse(v);
14428                     return parsed ? new Date(parsed) : null;
14429                 };
14430              break;
14431             
14432         }
14433         this.convert = cv;
14434     }
14435 };
14436
14437 Roo.data.Field.prototype = {
14438     dateFormat: null,
14439     defaultValue: "",
14440     mapping: null,
14441     sortType : null,
14442     sortDir : "ASC"
14443 };/*
14444  * Based on:
14445  * Ext JS Library 1.1.1
14446  * Copyright(c) 2006-2007, Ext JS, LLC.
14447  *
14448  * Originally Released Under LGPL - original licence link has changed is not relivant.
14449  *
14450  * Fork - LGPL
14451  * <script type="text/javascript">
14452  */
14453  
14454 // Base class for reading structured data from a data source.  This class is intended to be
14455 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14456
14457 /**
14458  * @class Roo.data.DataReader
14459  * Base class for reading structured data from a data source.  This class is intended to be
14460  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14461  */
14462
14463 Roo.data.DataReader = function(meta, recordType){
14464     
14465     this.meta = meta;
14466     
14467     this.recordType = recordType instanceof Array ? 
14468         Roo.data.Record.create(recordType) : recordType;
14469 };
14470
14471 Roo.data.DataReader.prototype = {
14472     
14473     
14474     readerType : 'Data',
14475      /**
14476      * Create an empty record
14477      * @param {Object} data (optional) - overlay some values
14478      * @return {Roo.data.Record} record created.
14479      */
14480     newRow :  function(d) {
14481         var da =  {};
14482         this.recordType.prototype.fields.each(function(c) {
14483             switch( c.type) {
14484                 case 'int' : da[c.name] = 0; break;
14485                 case 'date' : da[c.name] = new Date(); break;
14486                 case 'float' : da[c.name] = 0.0; break;
14487                 case 'boolean' : da[c.name] = false; break;
14488                 default : da[c.name] = ""; break;
14489             }
14490             
14491         });
14492         return new this.recordType(Roo.apply(da, d));
14493     }
14494     
14495     
14496 };/*
14497  * Based on:
14498  * Ext JS Library 1.1.1
14499  * Copyright(c) 2006-2007, Ext JS, LLC.
14500  *
14501  * Originally Released Under LGPL - original licence link has changed is not relivant.
14502  *
14503  * Fork - LGPL
14504  * <script type="text/javascript">
14505  */
14506
14507 /**
14508  * @class Roo.data.DataProxy
14509  * @extends Roo.data.Observable
14510  * This class is an abstract base class for implementations which provide retrieval of
14511  * unformatted data objects.<br>
14512  * <p>
14513  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14514  * (of the appropriate type which knows how to parse the data object) to provide a block of
14515  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14516  * <p>
14517  * Custom implementations must implement the load method as described in
14518  * {@link Roo.data.HttpProxy#load}.
14519  */
14520 Roo.data.DataProxy = function(){
14521     this.addEvents({
14522         /**
14523          * @event beforeload
14524          * Fires before a network request is made to retrieve a data object.
14525          * @param {Object} This DataProxy object.
14526          * @param {Object} params The params parameter to the load function.
14527          */
14528         beforeload : true,
14529         /**
14530          * @event load
14531          * Fires before the load method's callback is called.
14532          * @param {Object} This DataProxy object.
14533          * @param {Object} o The data object.
14534          * @param {Object} arg The callback argument object passed to the load function.
14535          */
14536         load : true,
14537         /**
14538          * @event loadexception
14539          * Fires if an Exception occurs during data retrieval.
14540          * @param {Object} This DataProxy object.
14541          * @param {Object} o The data object.
14542          * @param {Object} arg The callback argument object passed to the load function.
14543          * @param {Object} e The Exception.
14544          */
14545         loadexception : true
14546     });
14547     Roo.data.DataProxy.superclass.constructor.call(this);
14548 };
14549
14550 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14551
14552     /**
14553      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14554      */
14555 /*
14556  * Based on:
14557  * Ext JS Library 1.1.1
14558  * Copyright(c) 2006-2007, Ext JS, LLC.
14559  *
14560  * Originally Released Under LGPL - original licence link has changed is not relivant.
14561  *
14562  * Fork - LGPL
14563  * <script type="text/javascript">
14564  */
14565 /**
14566  * @class Roo.data.MemoryProxy
14567  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14568  * to the Reader when its load method is called.
14569  * @constructor
14570  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14571  */
14572 Roo.data.MemoryProxy = function(data){
14573     if (data.data) {
14574         data = data.data;
14575     }
14576     Roo.data.MemoryProxy.superclass.constructor.call(this);
14577     this.data = data;
14578 };
14579
14580 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14581     
14582     /**
14583      * Load data from the requested source (in this case an in-memory
14584      * data object passed to the constructor), read the data object into
14585      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14586      * process that block using the passed callback.
14587      * @param {Object} params This parameter is not used by the MemoryProxy class.
14588      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14589      * object into a block of Roo.data.Records.
14590      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14591      * The function must be passed <ul>
14592      * <li>The Record block object</li>
14593      * <li>The "arg" argument from the load function</li>
14594      * <li>A boolean success indicator</li>
14595      * </ul>
14596      * @param {Object} scope The scope in which to call the callback
14597      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14598      */
14599     load : function(params, reader, callback, scope, arg){
14600         params = params || {};
14601         var result;
14602         try {
14603             result = reader.readRecords(params.data ? params.data :this.data);
14604         }catch(e){
14605             this.fireEvent("loadexception", this, arg, null, e);
14606             callback.call(scope, null, arg, false);
14607             return;
14608         }
14609         callback.call(scope, result, arg, true);
14610     },
14611     
14612     // private
14613     update : function(params, records){
14614         
14615     }
14616 });/*
14617  * Based on:
14618  * Ext JS Library 1.1.1
14619  * Copyright(c) 2006-2007, Ext JS, LLC.
14620  *
14621  * Originally Released Under LGPL - original licence link has changed is not relivant.
14622  *
14623  * Fork - LGPL
14624  * <script type="text/javascript">
14625  */
14626 /**
14627  * @class Roo.data.HttpProxy
14628  * @extends Roo.data.DataProxy
14629  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14630  * configured to reference a certain URL.<br><br>
14631  * <p>
14632  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14633  * from which the running page was served.<br><br>
14634  * <p>
14635  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14636  * <p>
14637  * Be aware that to enable the browser to parse an XML document, the server must set
14638  * the Content-Type header in the HTTP response to "text/xml".
14639  * @constructor
14640  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14641  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14642  * will be used to make the request.
14643  */
14644 Roo.data.HttpProxy = function(conn){
14645     Roo.data.HttpProxy.superclass.constructor.call(this);
14646     // is conn a conn config or a real conn?
14647     this.conn = conn;
14648     this.useAjax = !conn || !conn.events;
14649   
14650 };
14651
14652 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14653     // thse are take from connection...
14654     
14655     /**
14656      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14657      */
14658     /**
14659      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14660      * extra parameters to each request made by this object. (defaults to undefined)
14661      */
14662     /**
14663      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14664      *  to each request made by this object. (defaults to undefined)
14665      */
14666     /**
14667      * @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)
14668      */
14669     /**
14670      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14671      */
14672      /**
14673      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14674      * @type Boolean
14675      */
14676   
14677
14678     /**
14679      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14680      * @type Boolean
14681      */
14682     /**
14683      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14684      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14685      * a finer-grained basis than the DataProxy events.
14686      */
14687     getConnection : function(){
14688         return this.useAjax ? Roo.Ajax : this.conn;
14689     },
14690
14691     /**
14692      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14693      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14694      * process that block using the passed callback.
14695      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14696      * for the request to the remote server.
14697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14698      * object into a block of Roo.data.Records.
14699      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14700      * The function must be passed <ul>
14701      * <li>The Record block object</li>
14702      * <li>The "arg" argument from the load function</li>
14703      * <li>A boolean success indicator</li>
14704      * </ul>
14705      * @param {Object} scope The scope in which to call the callback
14706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14707      */
14708     load : function(params, reader, callback, scope, arg){
14709         if(this.fireEvent("beforeload", this, params) !== false){
14710             var  o = {
14711                 params : params || {},
14712                 request: {
14713                     callback : callback,
14714                     scope : scope,
14715                     arg : arg
14716                 },
14717                 reader: reader,
14718                 callback : this.loadResponse,
14719                 scope: this
14720             };
14721             if(this.useAjax){
14722                 Roo.applyIf(o, this.conn);
14723                 if(this.activeRequest){
14724                     Roo.Ajax.abort(this.activeRequest);
14725                 }
14726                 this.activeRequest = Roo.Ajax.request(o);
14727             }else{
14728                 this.conn.request(o);
14729             }
14730         }else{
14731             callback.call(scope||this, null, arg, false);
14732         }
14733     },
14734
14735     // private
14736     loadResponse : function(o, success, response){
14737         delete this.activeRequest;
14738         if(!success){
14739             this.fireEvent("loadexception", this, o, response);
14740             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14741             return;
14742         }
14743         var result;
14744         try {
14745             result = o.reader.read(response);
14746         }catch(e){
14747             this.fireEvent("loadexception", this, o, response, e);
14748             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14749             return;
14750         }
14751         
14752         this.fireEvent("load", this, o, o.request.arg);
14753         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14754     },
14755
14756     // private
14757     update : function(dataSet){
14758
14759     },
14760
14761     // private
14762     updateResponse : function(dataSet){
14763
14764     }
14765 });/*
14766  * Based on:
14767  * Ext JS Library 1.1.1
14768  * Copyright(c) 2006-2007, Ext JS, LLC.
14769  *
14770  * Originally Released Under LGPL - original licence link has changed is not relivant.
14771  *
14772  * Fork - LGPL
14773  * <script type="text/javascript">
14774  */
14775
14776 /**
14777  * @class Roo.data.ScriptTagProxy
14778  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14779  * other than the originating domain of the running page.<br><br>
14780  * <p>
14781  * <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
14782  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14783  * <p>
14784  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14785  * source code that is used as the source inside a &lt;script> tag.<br><br>
14786  * <p>
14787  * In order for the browser to process the returned data, the server must wrap the data object
14788  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14789  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14790  * depending on whether the callback name was passed:
14791  * <p>
14792  * <pre><code>
14793 boolean scriptTag = false;
14794 String cb = request.getParameter("callback");
14795 if (cb != null) {
14796     scriptTag = true;
14797     response.setContentType("text/javascript");
14798 } else {
14799     response.setContentType("application/x-json");
14800 }
14801 Writer out = response.getWriter();
14802 if (scriptTag) {
14803     out.write(cb + "(");
14804 }
14805 out.print(dataBlock.toJsonString());
14806 if (scriptTag) {
14807     out.write(");");
14808 }
14809 </pre></code>
14810  *
14811  * @constructor
14812  * @param {Object} config A configuration object.
14813  */
14814 Roo.data.ScriptTagProxy = function(config){
14815     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14816     Roo.apply(this, config);
14817     this.head = document.getElementsByTagName("head")[0];
14818 };
14819
14820 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14821
14822 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14823     /**
14824      * @cfg {String} url The URL from which to request the data object.
14825      */
14826     /**
14827      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14828      */
14829     timeout : 30000,
14830     /**
14831      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14832      * the server the name of the callback function set up by the load call to process the returned data object.
14833      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14834      * javascript output which calls this named function passing the data object as its only parameter.
14835      */
14836     callbackParam : "callback",
14837     /**
14838      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14839      * name to the request.
14840      */
14841     nocache : true,
14842
14843     /**
14844      * Load data from the configured URL, read the data object into
14845      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14846      * process that block using the passed callback.
14847      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14848      * for the request to the remote server.
14849      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14850      * object into a block of Roo.data.Records.
14851      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14852      * The function must be passed <ul>
14853      * <li>The Record block object</li>
14854      * <li>The "arg" argument from the load function</li>
14855      * <li>A boolean success indicator</li>
14856      * </ul>
14857      * @param {Object} scope The scope in which to call the callback
14858      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14859      */
14860     load : function(params, reader, callback, scope, arg){
14861         if(this.fireEvent("beforeload", this, params) !== false){
14862
14863             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14864
14865             var url = this.url;
14866             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14867             if(this.nocache){
14868                 url += "&_dc=" + (new Date().getTime());
14869             }
14870             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14871             var trans = {
14872                 id : transId,
14873                 cb : "stcCallback"+transId,
14874                 scriptId : "stcScript"+transId,
14875                 params : params,
14876                 arg : arg,
14877                 url : url,
14878                 callback : callback,
14879                 scope : scope,
14880                 reader : reader
14881             };
14882             var conn = this;
14883
14884             window[trans.cb] = function(o){
14885                 conn.handleResponse(o, trans);
14886             };
14887
14888             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14889
14890             if(this.autoAbort !== false){
14891                 this.abort();
14892             }
14893
14894             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14895
14896             var script = document.createElement("script");
14897             script.setAttribute("src", url);
14898             script.setAttribute("type", "text/javascript");
14899             script.setAttribute("id", trans.scriptId);
14900             this.head.appendChild(script);
14901
14902             this.trans = trans;
14903         }else{
14904             callback.call(scope||this, null, arg, false);
14905         }
14906     },
14907
14908     // private
14909     isLoading : function(){
14910         return this.trans ? true : false;
14911     },
14912
14913     /**
14914      * Abort the current server request.
14915      */
14916     abort : function(){
14917         if(this.isLoading()){
14918             this.destroyTrans(this.trans);
14919         }
14920     },
14921
14922     // private
14923     destroyTrans : function(trans, isLoaded){
14924         this.head.removeChild(document.getElementById(trans.scriptId));
14925         clearTimeout(trans.timeoutId);
14926         if(isLoaded){
14927             window[trans.cb] = undefined;
14928             try{
14929                 delete window[trans.cb];
14930             }catch(e){}
14931         }else{
14932             // if hasn't been loaded, wait for load to remove it to prevent script error
14933             window[trans.cb] = function(){
14934                 window[trans.cb] = undefined;
14935                 try{
14936                     delete window[trans.cb];
14937                 }catch(e){}
14938             };
14939         }
14940     },
14941
14942     // private
14943     handleResponse : function(o, trans){
14944         this.trans = false;
14945         this.destroyTrans(trans, true);
14946         var result;
14947         try {
14948             result = trans.reader.readRecords(o);
14949         }catch(e){
14950             this.fireEvent("loadexception", this, o, trans.arg, e);
14951             trans.callback.call(trans.scope||window, null, trans.arg, false);
14952             return;
14953         }
14954         this.fireEvent("load", this, o, trans.arg);
14955         trans.callback.call(trans.scope||window, result, trans.arg, true);
14956     },
14957
14958     // private
14959     handleFailure : function(trans){
14960         this.trans = false;
14961         this.destroyTrans(trans, false);
14962         this.fireEvent("loadexception", this, null, trans.arg);
14963         trans.callback.call(trans.scope||window, null, trans.arg, false);
14964     }
14965 });/*
14966  * Based on:
14967  * Ext JS Library 1.1.1
14968  * Copyright(c) 2006-2007, Ext JS, LLC.
14969  *
14970  * Originally Released Under LGPL - original licence link has changed is not relivant.
14971  *
14972  * Fork - LGPL
14973  * <script type="text/javascript">
14974  */
14975
14976 /**
14977  * @class Roo.data.JsonReader
14978  * @extends Roo.data.DataReader
14979  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14980  * based on mappings in a provided Roo.data.Record constructor.
14981  * 
14982  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14983  * in the reply previously. 
14984  * 
14985  * <p>
14986  * Example code:
14987  * <pre><code>
14988 var RecordDef = Roo.data.Record.create([
14989     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14990     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14991 ]);
14992 var myReader = new Roo.data.JsonReader({
14993     totalProperty: "results",    // The property which contains the total dataset size (optional)
14994     root: "rows",                // The property which contains an Array of row objects
14995     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14996 }, RecordDef);
14997 </code></pre>
14998  * <p>
14999  * This would consume a JSON file like this:
15000  * <pre><code>
15001 { 'results': 2, 'rows': [
15002     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15003     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15004 }
15005 </code></pre>
15006  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15007  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15008  * paged from the remote server.
15009  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15010  * @cfg {String} root name of the property which contains the Array of row objects.
15011  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15012  * @cfg {Array} fields Array of field definition objects
15013  * @constructor
15014  * Create a new JsonReader
15015  * @param {Object} meta Metadata configuration options
15016  * @param {Object} recordType Either an Array of field definition objects,
15017  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15018  */
15019 Roo.data.JsonReader = function(meta, recordType){
15020     
15021     meta = meta || {};
15022     // set some defaults:
15023     Roo.applyIf(meta, {
15024         totalProperty: 'total',
15025         successProperty : 'success',
15026         root : 'data',
15027         id : 'id'
15028     });
15029     
15030     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15031 };
15032 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15033     
15034     readerType : 'Json',
15035     
15036     /**
15037      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15038      * Used by Store query builder to append _requestMeta to params.
15039      * 
15040      */
15041     metaFromRemote : false,
15042     /**
15043      * This method is only used by a DataProxy which has retrieved data from a remote server.
15044      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15045      * @return {Object} data A data block which is used by an Roo.data.Store object as
15046      * a cache of Roo.data.Records.
15047      */
15048     read : function(response){
15049         var json = response.responseText;
15050        
15051         var o = /* eval:var:o */ eval("("+json+")");
15052         if(!o) {
15053             throw {message: "JsonReader.read: Json object not found"};
15054         }
15055         
15056         if(o.metaData){
15057             
15058             delete this.ef;
15059             this.metaFromRemote = true;
15060             this.meta = o.metaData;
15061             this.recordType = Roo.data.Record.create(o.metaData.fields);
15062             this.onMetaChange(this.meta, this.recordType, o);
15063         }
15064         return this.readRecords(o);
15065     },
15066
15067     // private function a store will implement
15068     onMetaChange : function(meta, recordType, o){
15069
15070     },
15071
15072     /**
15073          * @ignore
15074          */
15075     simpleAccess: function(obj, subsc) {
15076         return obj[subsc];
15077     },
15078
15079         /**
15080          * @ignore
15081          */
15082     getJsonAccessor: function(){
15083         var re = /[\[\.]/;
15084         return function(expr) {
15085             try {
15086                 return(re.test(expr))
15087                     ? new Function("obj", "return obj." + expr)
15088                     : function(obj){
15089                         return obj[expr];
15090                     };
15091             } catch(e){}
15092             return Roo.emptyFn;
15093         };
15094     }(),
15095
15096     /**
15097      * Create a data block containing Roo.data.Records from an XML document.
15098      * @param {Object} o An object which contains an Array of row objects in the property specified
15099      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15100      * which contains the total size of the dataset.
15101      * @return {Object} data A data block which is used by an Roo.data.Store object as
15102      * a cache of Roo.data.Records.
15103      */
15104     readRecords : function(o){
15105         /**
15106          * After any data loads, the raw JSON data is available for further custom processing.
15107          * @type Object
15108          */
15109         this.o = o;
15110         var s = this.meta, Record = this.recordType,
15111             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15112
15113 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15114         if (!this.ef) {
15115             if(s.totalProperty) {
15116                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15117                 }
15118                 if(s.successProperty) {
15119                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15120                 }
15121                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15122                 if (s.id) {
15123                         var g = this.getJsonAccessor(s.id);
15124                         this.getId = function(rec) {
15125                                 var r = g(rec);  
15126                                 return (r === undefined || r === "") ? null : r;
15127                         };
15128                 } else {
15129                         this.getId = function(){return null;};
15130                 }
15131             this.ef = [];
15132             for(var jj = 0; jj < fl; jj++){
15133                 f = fi[jj];
15134                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15135                 this.ef[jj] = this.getJsonAccessor(map);
15136             }
15137         }
15138
15139         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15140         if(s.totalProperty){
15141             var vt = parseInt(this.getTotal(o), 10);
15142             if(!isNaN(vt)){
15143                 totalRecords = vt;
15144             }
15145         }
15146         if(s.successProperty){
15147             var vs = this.getSuccess(o);
15148             if(vs === false || vs === 'false'){
15149                 success = false;
15150             }
15151         }
15152         var records = [];
15153         for(var i = 0; i < c; i++){
15154                 var n = root[i];
15155             var values = {};
15156             var id = this.getId(n);
15157             for(var j = 0; j < fl; j++){
15158                 f = fi[j];
15159             var v = this.ef[j](n);
15160             if (!f.convert) {
15161                 Roo.log('missing convert for ' + f.name);
15162                 Roo.log(f);
15163                 continue;
15164             }
15165             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15166             }
15167             var record = new Record(values, id);
15168             record.json = n;
15169             records[i] = record;
15170         }
15171         return {
15172             raw : o,
15173             success : success,
15174             records : records,
15175             totalRecords : totalRecords
15176         };
15177     },
15178     // used when loading children.. @see loadDataFromChildren
15179     toLoadData: function(rec)
15180     {
15181         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15182         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15183         return { data : data, total : data.length };
15184         
15185     }
15186 });/*
15187  * Based on:
15188  * Ext JS Library 1.1.1
15189  * Copyright(c) 2006-2007, Ext JS, LLC.
15190  *
15191  * Originally Released Under LGPL - original licence link has changed is not relivant.
15192  *
15193  * Fork - LGPL
15194  * <script type="text/javascript">
15195  */
15196
15197 /**
15198  * @class Roo.data.ArrayReader
15199  * @extends Roo.data.DataReader
15200  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15201  * Each element of that Array represents a row of data fields. The
15202  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15203  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15204  * <p>
15205  * Example code:.
15206  * <pre><code>
15207 var RecordDef = Roo.data.Record.create([
15208     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15209     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15210 ]);
15211 var myReader = new Roo.data.ArrayReader({
15212     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15213 }, RecordDef);
15214 </code></pre>
15215  * <p>
15216  * This would consume an Array like this:
15217  * <pre><code>
15218 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15219   </code></pre>
15220  
15221  * @constructor
15222  * Create a new JsonReader
15223  * @param {Object} meta Metadata configuration options.
15224  * @param {Object|Array} recordType Either an Array of field definition objects
15225  * 
15226  * @cfg {Array} fields Array of field definition objects
15227  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15228  * as specified to {@link Roo.data.Record#create},
15229  * or an {@link Roo.data.Record} object
15230  *
15231  * 
15232  * created using {@link Roo.data.Record#create}.
15233  */
15234 Roo.data.ArrayReader = function(meta, recordType)
15235 {    
15236     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15237 };
15238
15239 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15240     
15241       /**
15242      * Create a data block containing Roo.data.Records from an XML document.
15243      * @param {Object} o An Array of row objects which represents the dataset.
15244      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15245      * a cache of Roo.data.Records.
15246      */
15247     readRecords : function(o)
15248     {
15249         var sid = this.meta ? this.meta.id : null;
15250         var recordType = this.recordType, fields = recordType.prototype.fields;
15251         var records = [];
15252         var root = o;
15253         for(var i = 0; i < root.length; i++){
15254             var n = root[i];
15255             var values = {};
15256             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15257             for(var j = 0, jlen = fields.length; j < jlen; j++){
15258                 var f = fields.items[j];
15259                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15260                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15261                 v = f.convert(v);
15262                 values[f.name] = v;
15263             }
15264             var record = new recordType(values, id);
15265             record.json = n;
15266             records[records.length] = record;
15267         }
15268         return {
15269             records : records,
15270             totalRecords : records.length
15271         };
15272     },
15273     // used when loading children.. @see loadDataFromChildren
15274     toLoadData: function(rec)
15275     {
15276         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15277         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15278         
15279     }
15280     
15281     
15282 });/*
15283  * - LGPL
15284  * * 
15285  */
15286
15287 /**
15288  * @class Roo.bootstrap.ComboBox
15289  * @extends Roo.bootstrap.TriggerField
15290  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15291  * @cfg {Boolean} append (true|false) default false
15292  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15293  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15294  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15295  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15296  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15297  * @cfg {Boolean} animate default true
15298  * @cfg {Boolean} emptyResultText only for touch device
15299  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15300  * @cfg {String} emptyTitle default ''
15301  * @cfg {Number} width fixed with? experimental
15302  * @constructor
15303  * Create a new ComboBox.
15304  * @param {Object} config Configuration options
15305  */
15306 Roo.bootstrap.ComboBox = function(config){
15307     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15308     this.addEvents({
15309         /**
15310          * @event expand
15311          * Fires when the dropdown list is expanded
15312         * @param {Roo.bootstrap.ComboBox} combo This combo box
15313         */
15314         'expand' : true,
15315         /**
15316          * @event collapse
15317          * Fires when the dropdown list is collapsed
15318         * @param {Roo.bootstrap.ComboBox} combo This combo box
15319         */
15320         'collapse' : true,
15321         /**
15322          * @event beforeselect
15323          * Fires before a list item is selected. Return false to cancel the selection.
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         * @param {Roo.data.Record} record The data record returned from the underlying store
15326         * @param {Number} index The index of the selected item in the dropdown list
15327         */
15328         'beforeselect' : true,
15329         /**
15330          * @event select
15331          * Fires when a list item is selected
15332         * @param {Roo.bootstrap.ComboBox} combo This combo box
15333         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15334         * @param {Number} index The index of the selected item in the dropdown list
15335         */
15336         'select' : true,
15337         /**
15338          * @event beforequery
15339          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15340          * The event object passed has these properties:
15341         * @param {Roo.bootstrap.ComboBox} combo This combo box
15342         * @param {String} query The query
15343         * @param {Boolean} forceAll true to force "all" query
15344         * @param {Boolean} cancel true to cancel the query
15345         * @param {Object} e The query event object
15346         */
15347         'beforequery': true,
15348          /**
15349          * @event add
15350          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15351         * @param {Roo.bootstrap.ComboBox} combo This combo box
15352         */
15353         'add' : true,
15354         /**
15355          * @event edit
15356          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15357         * @param {Roo.bootstrap.ComboBox} combo This combo box
15358         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15359         */
15360         'edit' : true,
15361         /**
15362          * @event remove
15363          * Fires when the remove value from the combobox array
15364         * @param {Roo.bootstrap.ComboBox} combo This combo box
15365         */
15366         'remove' : true,
15367         /**
15368          * @event afterremove
15369          * Fires when the remove value from the combobox array
15370         * @param {Roo.bootstrap.ComboBox} combo This combo box
15371         */
15372         'afterremove' : true,
15373         /**
15374          * @event specialfilter
15375          * Fires when specialfilter
15376             * @param {Roo.bootstrap.ComboBox} combo This combo box
15377             */
15378         'specialfilter' : true,
15379         /**
15380          * @event tick
15381          * Fires when tick the element
15382             * @param {Roo.bootstrap.ComboBox} combo This combo box
15383             */
15384         'tick' : true,
15385         /**
15386          * @event touchviewdisplay
15387          * Fires when touch view require special display (default is using displayField)
15388             * @param {Roo.bootstrap.ComboBox} combo This combo box
15389             * @param {Object} cfg set html .
15390             */
15391         'touchviewdisplay' : true
15392         
15393     });
15394     
15395     this.item = [];
15396     this.tickItems = [];
15397     
15398     this.selectedIndex = -1;
15399     if(this.mode == 'local'){
15400         if(config.queryDelay === undefined){
15401             this.queryDelay = 10;
15402         }
15403         if(config.minChars === undefined){
15404             this.minChars = 0;
15405         }
15406     }
15407 };
15408
15409 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15410      
15411     /**
15412      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15413      * rendering into an Roo.Editor, defaults to false)
15414      */
15415     /**
15416      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15417      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15418      */
15419     /**
15420      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15421      */
15422     /**
15423      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15424      * the dropdown list (defaults to undefined, with no header element)
15425      */
15426
15427      /**
15428      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15429      */
15430      
15431      /**
15432      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15433      */
15434     listWidth: undefined,
15435     /**
15436      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15437      * mode = 'remote' or 'text' if mode = 'local')
15438      */
15439     displayField: undefined,
15440     
15441     /**
15442      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15443      * mode = 'remote' or 'value' if mode = 'local'). 
15444      * Note: use of a valueField requires the user make a selection
15445      * in order for a value to be mapped.
15446      */
15447     valueField: undefined,
15448     /**
15449      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15450      */
15451     modalTitle : '',
15452     
15453     /**
15454      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15455      * field's data value (defaults to the underlying DOM element's name)
15456      */
15457     hiddenName: undefined,
15458     /**
15459      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15460      */
15461     listClass: '',
15462     /**
15463      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15464      */
15465     selectedClass: 'active',
15466     
15467     /**
15468      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15469      */
15470     shadow:'sides',
15471     /**
15472      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15473      * anchor positions (defaults to 'tl-bl')
15474      */
15475     listAlign: 'tl-bl?',
15476     /**
15477      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15478      */
15479     maxHeight: 300,
15480     /**
15481      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15482      * query specified by the allQuery config option (defaults to 'query')
15483      */
15484     triggerAction: 'query',
15485     /**
15486      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15487      * (defaults to 4, does not apply if editable = false)
15488      */
15489     minChars : 4,
15490     /**
15491      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15492      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15493      */
15494     typeAhead: false,
15495     /**
15496      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15497      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15498      */
15499     queryDelay: 500,
15500     /**
15501      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15502      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15503      */
15504     pageSize: 0,
15505     /**
15506      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15507      * when editable = true (defaults to false)
15508      */
15509     selectOnFocus:false,
15510     /**
15511      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15512      */
15513     queryParam: 'query',
15514     /**
15515      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15516      * when mode = 'remote' (defaults to 'Loading...')
15517      */
15518     loadingText: 'Loading...',
15519     /**
15520      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15521      */
15522     resizable: false,
15523     /**
15524      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15525      */
15526     handleHeight : 8,
15527     /**
15528      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15529      * traditional select (defaults to true)
15530      */
15531     editable: true,
15532     /**
15533      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15534      */
15535     allQuery: '',
15536     /**
15537      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15538      */
15539     mode: 'remote',
15540     /**
15541      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15542      * listWidth has a higher value)
15543      */
15544     minListWidth : 70,
15545     /**
15546      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15547      * allow the user to set arbitrary text into the field (defaults to false)
15548      */
15549     forceSelection:false,
15550     /**
15551      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15552      * if typeAhead = true (defaults to 250)
15553      */
15554     typeAheadDelay : 250,
15555     /**
15556      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15557      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15558      */
15559     valueNotFoundText : undefined,
15560     /**
15561      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15562      */
15563     blockFocus : false,
15564     
15565     /**
15566      * @cfg {Boolean} disableClear Disable showing of clear button.
15567      */
15568     disableClear : false,
15569     /**
15570      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15571      */
15572     alwaysQuery : false,
15573     
15574     /**
15575      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15576      */
15577     multiple : false,
15578     
15579     /**
15580      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15581      */
15582     invalidClass : "has-warning",
15583     
15584     /**
15585      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15586      */
15587     validClass : "has-success",
15588     
15589     /**
15590      * @cfg {Boolean} specialFilter (true|false) special filter default false
15591      */
15592     specialFilter : false,
15593     
15594     /**
15595      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15596      */
15597     mobileTouchView : true,
15598     
15599     /**
15600      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15601      */
15602     useNativeIOS : false,
15603     
15604     /**
15605      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15606      */
15607     mobile_restrict_height : false,
15608     
15609     ios_options : false,
15610     
15611     //private
15612     addicon : false,
15613     editicon: false,
15614     
15615     page: 0,
15616     hasQuery: false,
15617     append: false,
15618     loadNext: false,
15619     autoFocus : true,
15620     tickable : false,
15621     btnPosition : 'right',
15622     triggerList : true,
15623     showToggleBtn : true,
15624     animate : true,
15625     emptyResultText: 'Empty',
15626     triggerText : 'Select',
15627     emptyTitle : '',
15628     width : false,
15629     
15630     // element that contains real text value.. (when hidden is used..)
15631     
15632     getAutoCreate : function()
15633     {   
15634         var cfg = false;
15635         //render
15636         /*
15637          * Render classic select for iso
15638          */
15639         
15640         if(Roo.isIOS && this.useNativeIOS){
15641             cfg = this.getAutoCreateNativeIOS();
15642             return cfg;
15643         }
15644         
15645         /*
15646          * Touch Devices
15647          */
15648         
15649         if(Roo.isTouch && this.mobileTouchView){
15650             cfg = this.getAutoCreateTouchView();
15651             return cfg;;
15652         }
15653         
15654         /*
15655          *  Normal ComboBox
15656          */
15657         if(!this.tickable){
15658             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15659             return cfg;
15660         }
15661         
15662         /*
15663          *  ComboBox with tickable selections
15664          */
15665              
15666         var align = this.labelAlign || this.parentLabelAlign();
15667         
15668         cfg = {
15669             cls : 'form-group roo-combobox-tickable' //input-group
15670         };
15671         
15672         var btn_text_select = '';
15673         var btn_text_done = '';
15674         var btn_text_cancel = '';
15675         
15676         if (this.btn_text_show) {
15677             btn_text_select = 'Select';
15678             btn_text_done = 'Done';
15679             btn_text_cancel = 'Cancel'; 
15680         }
15681         
15682         var buttons = {
15683             tag : 'div',
15684             cls : 'tickable-buttons',
15685             cn : [
15686                 {
15687                     tag : 'button',
15688                     type : 'button',
15689                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15690                     //html : this.triggerText
15691                     html: btn_text_select
15692                 },
15693                 {
15694                     tag : 'button',
15695                     type : 'button',
15696                     name : 'ok',
15697                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15698                     //html : 'Done'
15699                     html: btn_text_done
15700                 },
15701                 {
15702                     tag : 'button',
15703                     type : 'button',
15704                     name : 'cancel',
15705                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15706                     //html : 'Cancel'
15707                     html: btn_text_cancel
15708                 }
15709             ]
15710         };
15711         
15712         if(this.editable){
15713             buttons.cn.unshift({
15714                 tag: 'input',
15715                 cls: 'roo-select2-search-field-input'
15716             });
15717         }
15718         
15719         var _this = this;
15720         
15721         Roo.each(buttons.cn, function(c){
15722             if (_this.size) {
15723                 c.cls += ' btn-' + _this.size;
15724             }
15725
15726             if (_this.disabled) {
15727                 c.disabled = true;
15728             }
15729         });
15730         
15731         var box = {
15732             tag: 'div',
15733             style : 'display: contents',
15734             cn: [
15735                 {
15736                     tag: 'input',
15737                     type : 'hidden',
15738                     cls: 'form-hidden-field'
15739                 },
15740                 {
15741                     tag: 'ul',
15742                     cls: 'roo-select2-choices',
15743                     cn:[
15744                         {
15745                             tag: 'li',
15746                             cls: 'roo-select2-search-field',
15747                             cn: [
15748                                 buttons
15749                             ]
15750                         }
15751                     ]
15752                 }
15753             ]
15754         };
15755         
15756         var combobox = {
15757             cls: 'roo-select2-container input-group roo-select2-container-multi',
15758             cn: [
15759                 
15760                 box
15761 //                {
15762 //                    tag: 'ul',
15763 //                    cls: 'typeahead typeahead-long dropdown-menu',
15764 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15765 //                }
15766             ]
15767         };
15768         
15769         if(this.hasFeedback && !this.allowBlank){
15770             
15771             var feedback = {
15772                 tag: 'span',
15773                 cls: 'glyphicon form-control-feedback'
15774             };
15775
15776             combobox.cn.push(feedback);
15777         }
15778         
15779         
15780         
15781         var indicator = {
15782             tag : 'i',
15783             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15784             tooltip : 'This field is required'
15785         };
15786         if (Roo.bootstrap.version == 4) {
15787             indicator = {
15788                 tag : 'i',
15789                 style : 'display:none'
15790             };
15791         }
15792         if (align ==='left' && this.fieldLabel.length) {
15793             
15794             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15795             
15796             cfg.cn = [
15797                 indicator,
15798                 {
15799                     tag: 'label',
15800                     'for' :  id,
15801                     cls : 'control-label col-form-label',
15802                     html : this.fieldLabel
15803
15804                 },
15805                 {
15806                     cls : "", 
15807                     cn: [
15808                         combobox
15809                     ]
15810                 }
15811
15812             ];
15813             
15814             var labelCfg = cfg.cn[1];
15815             var contentCfg = cfg.cn[2];
15816             
15817
15818             if(this.indicatorpos == 'right'){
15819                 
15820                 cfg.cn = [
15821                     {
15822                         tag: 'label',
15823                         'for' :  id,
15824                         cls : 'control-label col-form-label',
15825                         cn : [
15826                             {
15827                                 tag : 'span',
15828                                 html : this.fieldLabel
15829                             },
15830                             indicator
15831                         ]
15832                     },
15833                     {
15834                         cls : "",
15835                         cn: [
15836                             combobox
15837                         ]
15838                     }
15839
15840                 ];
15841                 
15842                 
15843                 
15844                 labelCfg = cfg.cn[0];
15845                 contentCfg = cfg.cn[1];
15846             
15847             }
15848             
15849             if(this.labelWidth > 12){
15850                 labelCfg.style = "width: " + this.labelWidth + 'px';
15851             }
15852             if(this.width * 1 > 0){
15853                 contentCfg.style = "width: " + this.width + 'px';
15854             }
15855             if(this.labelWidth < 13 && this.labelmd == 0){
15856                 this.labelmd = this.labelWidth;
15857             }
15858             
15859             if(this.labellg > 0){
15860                 labelCfg.cls += ' col-lg-' + this.labellg;
15861                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15862             }
15863             
15864             if(this.labelmd > 0){
15865                 labelCfg.cls += ' col-md-' + this.labelmd;
15866                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15867             }
15868             
15869             if(this.labelsm > 0){
15870                 labelCfg.cls += ' col-sm-' + this.labelsm;
15871                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15872             }
15873             
15874             if(this.labelxs > 0){
15875                 labelCfg.cls += ' col-xs-' + this.labelxs;
15876                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15877             }
15878                 
15879                 
15880         } else if ( this.fieldLabel.length) {
15881 //                Roo.log(" label");
15882                  cfg.cn = [
15883                    indicator,
15884                     {
15885                         tag: 'label',
15886                         //cls : 'input-group-addon',
15887                         html : this.fieldLabel
15888                     },
15889                     combobox
15890                 ];
15891                 
15892                 if(this.indicatorpos == 'right'){
15893                     cfg.cn = [
15894                         {
15895                             tag: 'label',
15896                             //cls : 'input-group-addon',
15897                             html : this.fieldLabel
15898                         },
15899                         indicator,
15900                         combobox
15901                     ];
15902                     
15903                 }
15904
15905         } else {
15906             
15907 //                Roo.log(" no label && no align");
15908                 cfg = combobox
15909                      
15910                 
15911         }
15912          
15913         var settings=this;
15914         ['xs','sm','md','lg'].map(function(size){
15915             if (settings[size]) {
15916                 cfg.cls += ' col-' + size + '-' + settings[size];
15917             }
15918         });
15919         
15920         return cfg;
15921         
15922     },
15923     
15924     _initEventsCalled : false,
15925     
15926     // private
15927     initEvents: function()
15928     {   
15929         if (this._initEventsCalled) { // as we call render... prevent looping...
15930             return;
15931         }
15932         this._initEventsCalled = true;
15933         
15934         if (!this.store) {
15935             throw "can not find store for combo";
15936         }
15937         
15938         this.indicator = this.indicatorEl();
15939         
15940         this.store = Roo.factory(this.store, Roo.data);
15941         this.store.parent = this;
15942         
15943         // if we are building from html. then this element is so complex, that we can not really
15944         // use the rendered HTML.
15945         // so we have to trash and replace the previous code.
15946         if (Roo.XComponent.build_from_html) {
15947             // remove this element....
15948             var e = this.el.dom, k=0;
15949             while (e ) { e = e.previousSibling;  ++k;}
15950
15951             this.el.remove();
15952             
15953             this.el=false;
15954             this.rendered = false;
15955             
15956             this.render(this.parent().getChildContainer(true), k);
15957         }
15958         
15959         if(Roo.isIOS && this.useNativeIOS){
15960             this.initIOSView();
15961             return;
15962         }
15963         
15964         /*
15965          * Touch Devices
15966          */
15967         
15968         if(Roo.isTouch && this.mobileTouchView){
15969             this.initTouchView();
15970             return;
15971         }
15972         
15973         if(this.tickable){
15974             this.initTickableEvents();
15975             return;
15976         }
15977         
15978         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15979         
15980         if(this.hiddenName){
15981             
15982             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15983             
15984             this.hiddenField.dom.value =
15985                 this.hiddenValue !== undefined ? this.hiddenValue :
15986                 this.value !== undefined ? this.value : '';
15987
15988             // prevent input submission
15989             this.el.dom.removeAttribute('name');
15990             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15991              
15992              
15993         }
15994         //if(Roo.isGecko){
15995         //    this.el.dom.setAttribute('autocomplete', 'off');
15996         //}
15997         
15998         var cls = 'x-combo-list';
15999         
16000         //this.list = new Roo.Layer({
16001         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16002         //});
16003         
16004         var _this = this;
16005         
16006         (function(){
16007             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16008             _this.list.setWidth(lw);
16009         }).defer(100);
16010         
16011         this.list.on('mouseover', this.onViewOver, this);
16012         this.list.on('mousemove', this.onViewMove, this);
16013         this.list.on('scroll', this.onViewScroll, this);
16014         
16015         /*
16016         this.list.swallowEvent('mousewheel');
16017         this.assetHeight = 0;
16018
16019         if(this.title){
16020             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16021             this.assetHeight += this.header.getHeight();
16022         }
16023
16024         this.innerList = this.list.createChild({cls:cls+'-inner'});
16025         this.innerList.on('mouseover', this.onViewOver, this);
16026         this.innerList.on('mousemove', this.onViewMove, this);
16027         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16028         
16029         if(this.allowBlank && !this.pageSize && !this.disableClear){
16030             this.footer = this.list.createChild({cls:cls+'-ft'});
16031             this.pageTb = new Roo.Toolbar(this.footer);
16032            
16033         }
16034         if(this.pageSize){
16035             this.footer = this.list.createChild({cls:cls+'-ft'});
16036             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16037                     {pageSize: this.pageSize});
16038             
16039         }
16040         
16041         if (this.pageTb && this.allowBlank && !this.disableClear) {
16042             var _this = this;
16043             this.pageTb.add(new Roo.Toolbar.Fill(), {
16044                 cls: 'x-btn-icon x-btn-clear',
16045                 text: '&#160;',
16046                 handler: function()
16047                 {
16048                     _this.collapse();
16049                     _this.clearValue();
16050                     _this.onSelect(false, -1);
16051                 }
16052             });
16053         }
16054         if (this.footer) {
16055             this.assetHeight += this.footer.getHeight();
16056         }
16057         */
16058             
16059         if(!this.tpl){
16060             this.tpl = Roo.bootstrap.version == 4 ?
16061                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16062                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16063         }
16064
16065         this.view = new Roo.View(this.list, this.tpl, {
16066             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16067         });
16068         //this.view.wrapEl.setDisplayed(false);
16069         this.view.on('click', this.onViewClick, this);
16070         
16071         
16072         this.store.on('beforeload', this.onBeforeLoad, this);
16073         this.store.on('load', this.onLoad, this);
16074         this.store.on('loadexception', this.onLoadException, this);
16075         /*
16076         if(this.resizable){
16077             this.resizer = new Roo.Resizable(this.list,  {
16078                pinned:true, handles:'se'
16079             });
16080             this.resizer.on('resize', function(r, w, h){
16081                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16082                 this.listWidth = w;
16083                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16084                 this.restrictHeight();
16085             }, this);
16086             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16087         }
16088         */
16089         if(!this.editable){
16090             this.editable = true;
16091             this.setEditable(false);
16092         }
16093         
16094         /*
16095         
16096         if (typeof(this.events.add.listeners) != 'undefined') {
16097             
16098             this.addicon = this.wrap.createChild(
16099                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16100        
16101             this.addicon.on('click', function(e) {
16102                 this.fireEvent('add', this);
16103             }, this);
16104         }
16105         if (typeof(this.events.edit.listeners) != 'undefined') {
16106             
16107             this.editicon = this.wrap.createChild(
16108                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16109             if (this.addicon) {
16110                 this.editicon.setStyle('margin-left', '40px');
16111             }
16112             this.editicon.on('click', function(e) {
16113                 
16114                 // we fire even  if inothing is selected..
16115                 this.fireEvent('edit', this, this.lastData );
16116                 
16117             }, this);
16118         }
16119         */
16120         
16121         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16122             "up" : function(e){
16123                 this.inKeyMode = true;
16124                 this.selectPrev();
16125             },
16126
16127             "down" : function(e){
16128                 if(!this.isExpanded()){
16129                     this.onTriggerClick();
16130                 }else{
16131                     this.inKeyMode = true;
16132                     this.selectNext();
16133                 }
16134             },
16135
16136             "enter" : function(e){
16137 //                this.onViewClick();
16138                 //return true;
16139                 this.collapse();
16140                 
16141                 if(this.fireEvent("specialkey", this, e)){
16142                     this.onViewClick(false);
16143                 }
16144                 
16145                 return true;
16146             },
16147
16148             "esc" : function(e){
16149                 this.collapse();
16150             },
16151
16152             "tab" : function(e){
16153                 this.collapse();
16154                 
16155                 if(this.fireEvent("specialkey", this, e)){
16156                     this.onViewClick(false);
16157                 }
16158                 
16159                 return true;
16160             },
16161
16162             scope : this,
16163
16164             doRelay : function(foo, bar, hname){
16165                 if(hname == 'down' || this.scope.isExpanded()){
16166                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16167                 }
16168                 return true;
16169             },
16170
16171             forceKeyDown: true
16172         });
16173         
16174         
16175         this.queryDelay = Math.max(this.queryDelay || 10,
16176                 this.mode == 'local' ? 10 : 250);
16177         
16178         
16179         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16180         
16181         if(this.typeAhead){
16182             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16183         }
16184         if(this.editable !== false){
16185             this.inputEl().on("keyup", this.onKeyUp, this);
16186         }
16187         if(this.forceSelection){
16188             this.inputEl().on('blur', this.doForce, this);
16189         }
16190         
16191         if(this.multiple){
16192             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16193             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16194         }
16195     },
16196     
16197     initTickableEvents: function()
16198     {   
16199         this.createList();
16200         
16201         if(this.hiddenName){
16202             
16203             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16204             
16205             this.hiddenField.dom.value =
16206                 this.hiddenValue !== undefined ? this.hiddenValue :
16207                 this.value !== undefined ? this.value : '';
16208
16209             // prevent input submission
16210             this.el.dom.removeAttribute('name');
16211             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16212              
16213              
16214         }
16215         
16216 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16217         
16218         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16219         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16220         if(this.triggerList){
16221             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16222         }
16223          
16224         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16225         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16226         
16227         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16228         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16229         
16230         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16231         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16232         
16233         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16234         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16235         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16236         
16237         this.okBtn.hide();
16238         this.cancelBtn.hide();
16239         
16240         var _this = this;
16241         
16242         (function(){
16243             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16244             _this.list.setWidth(lw);
16245         }).defer(100);
16246         
16247         this.list.on('mouseover', this.onViewOver, this);
16248         this.list.on('mousemove', this.onViewMove, this);
16249         
16250         this.list.on('scroll', this.onViewScroll, this);
16251         
16252         if(!this.tpl){
16253             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16254                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16255         }
16256
16257         this.view = new Roo.View(this.list, this.tpl, {
16258             singleSelect:true,
16259             tickable:true,
16260             parent:this,
16261             store: this.store,
16262             selectedClass: this.selectedClass
16263         });
16264         
16265         //this.view.wrapEl.setDisplayed(false);
16266         this.view.on('click', this.onViewClick, this);
16267         
16268         
16269         
16270         this.store.on('beforeload', this.onBeforeLoad, this);
16271         this.store.on('load', this.onLoad, this);
16272         this.store.on('loadexception', this.onLoadException, this);
16273         
16274         if(this.editable){
16275             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16276                 "up" : function(e){
16277                     this.inKeyMode = true;
16278                     this.selectPrev();
16279                 },
16280
16281                 "down" : function(e){
16282                     this.inKeyMode = true;
16283                     this.selectNext();
16284                 },
16285
16286                 "enter" : function(e){
16287                     if(this.fireEvent("specialkey", this, e)){
16288                         this.onViewClick(false);
16289                     }
16290                     
16291                     return true;
16292                 },
16293
16294                 "esc" : function(e){
16295                     this.onTickableFooterButtonClick(e, false, false);
16296                 },
16297
16298                 "tab" : function(e){
16299                     this.fireEvent("specialkey", this, e);
16300                     
16301                     this.onTickableFooterButtonClick(e, false, false);
16302                     
16303                     return true;
16304                 },
16305
16306                 scope : this,
16307
16308                 doRelay : function(e, fn, key){
16309                     if(this.scope.isExpanded()){
16310                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16311                     }
16312                     return true;
16313                 },
16314
16315                 forceKeyDown: true
16316             });
16317         }
16318         
16319         this.queryDelay = Math.max(this.queryDelay || 10,
16320                 this.mode == 'local' ? 10 : 250);
16321         
16322         
16323         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16324         
16325         if(this.typeAhead){
16326             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16327         }
16328         
16329         if(this.editable !== false){
16330             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16331         }
16332         
16333         this.indicator = this.indicatorEl();
16334         
16335         if(this.indicator){
16336             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16337             this.indicator.hide();
16338         }
16339         
16340     },
16341
16342     onDestroy : function(){
16343         if(this.view){
16344             this.view.setStore(null);
16345             this.view.el.removeAllListeners();
16346             this.view.el.remove();
16347             this.view.purgeListeners();
16348         }
16349         if(this.list){
16350             this.list.dom.innerHTML  = '';
16351         }
16352         
16353         if(this.store){
16354             this.store.un('beforeload', this.onBeforeLoad, this);
16355             this.store.un('load', this.onLoad, this);
16356             this.store.un('loadexception', this.onLoadException, this);
16357         }
16358         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16359     },
16360
16361     // private
16362     fireKey : function(e){
16363         if(e.isNavKeyPress() && !this.list.isVisible()){
16364             this.fireEvent("specialkey", this, e);
16365         }
16366     },
16367
16368     // private
16369     onResize: function(w, h)
16370     {
16371         
16372         
16373 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16374 //        
16375 //        if(typeof w != 'number'){
16376 //            // we do not handle it!?!?
16377 //            return;
16378 //        }
16379 //        var tw = this.trigger.getWidth();
16380 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16381 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16382 //        var x = w - tw;
16383 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16384 //            
16385 //        //this.trigger.setStyle('left', x+'px');
16386 //        
16387 //        if(this.list && this.listWidth === undefined){
16388 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16389 //            this.list.setWidth(lw);
16390 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16391 //        }
16392         
16393     
16394         
16395     },
16396
16397     /**
16398      * Allow or prevent the user from directly editing the field text.  If false is passed,
16399      * the user will only be able to select from the items defined in the dropdown list.  This method
16400      * is the runtime equivalent of setting the 'editable' config option at config time.
16401      * @param {Boolean} value True to allow the user to directly edit the field text
16402      */
16403     setEditable : function(value){
16404         if(value == this.editable){
16405             return;
16406         }
16407         this.editable = value;
16408         if(!value){
16409             this.inputEl().dom.setAttribute('readOnly', true);
16410             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16411             this.inputEl().addClass('x-combo-noedit');
16412         }else{
16413             this.inputEl().dom.removeAttribute('readOnly');
16414             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16415             this.inputEl().removeClass('x-combo-noedit');
16416         }
16417     },
16418
16419     // private
16420     
16421     onBeforeLoad : function(combo,opts){
16422         if(!this.hasFocus){
16423             return;
16424         }
16425          if (!opts.add) {
16426             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16427          }
16428         this.restrictHeight();
16429         this.selectedIndex = -1;
16430     },
16431
16432     // private
16433     onLoad : function(){
16434         
16435         this.hasQuery = false;
16436         
16437         if(!this.hasFocus){
16438             return;
16439         }
16440         
16441         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442             this.loading.hide();
16443         }
16444         
16445         if(this.store.getCount() > 0){
16446             
16447             this.expand();
16448             this.restrictHeight();
16449             if(this.lastQuery == this.allQuery){
16450                 if(this.editable && !this.tickable){
16451                     this.inputEl().dom.select();
16452                 }
16453                 
16454                 if(
16455                     !this.selectByValue(this.value, true) &&
16456                     this.autoFocus && 
16457                     (
16458                         !this.store.lastOptions ||
16459                         typeof(this.store.lastOptions.add) == 'undefined' || 
16460                         this.store.lastOptions.add != true
16461                     )
16462                 ){
16463                     this.select(0, true);
16464                 }
16465             }else{
16466                 if(this.autoFocus){
16467                     this.selectNext();
16468                 }
16469                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16470                     this.taTask.delay(this.typeAheadDelay);
16471                 }
16472             }
16473         }else{
16474             this.onEmptyResults();
16475         }
16476         
16477         //this.el.focus();
16478     },
16479     // private
16480     onLoadException : function()
16481     {
16482         this.hasQuery = false;
16483         
16484         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16485             this.loading.hide();
16486         }
16487         
16488         if(this.tickable && this.editable){
16489             return;
16490         }
16491         
16492         this.collapse();
16493         // only causes errors at present
16494         //Roo.log(this.store.reader.jsonData);
16495         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16496             // fixme
16497             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16498         //}
16499         
16500         
16501     },
16502     // private
16503     onTypeAhead : function(){
16504         if(this.store.getCount() > 0){
16505             var r = this.store.getAt(0);
16506             var newValue = r.data[this.displayField];
16507             var len = newValue.length;
16508             var selStart = this.getRawValue().length;
16509             
16510             if(selStart != len){
16511                 this.setRawValue(newValue);
16512                 this.selectText(selStart, newValue.length);
16513             }
16514         }
16515     },
16516
16517     // private
16518     onSelect : function(record, index){
16519         
16520         if(this.fireEvent('beforeselect', this, record, index) !== false){
16521         
16522             this.setFromData(index > -1 ? record.data : false);
16523             
16524             this.collapse();
16525             this.fireEvent('select', this, record, index);
16526         }
16527     },
16528
16529     /**
16530      * Returns the currently selected field value or empty string if no value is set.
16531      * @return {String} value The selected value
16532      */
16533     getValue : function()
16534     {
16535         if(Roo.isIOS && this.useNativeIOS){
16536             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16537         }
16538         
16539         if(this.multiple){
16540             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16541         }
16542         
16543         if(this.valueField){
16544             return typeof this.value != 'undefined' ? this.value : '';
16545         }else{
16546             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16547         }
16548     },
16549     
16550     getRawValue : function()
16551     {
16552         if(Roo.isIOS && this.useNativeIOS){
16553             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16554         }
16555         
16556         var v = this.inputEl().getValue();
16557         
16558         return v;
16559     },
16560
16561     /**
16562      * Clears any text/value currently set in the field
16563      */
16564     clearValue : function(){
16565         
16566         if(this.hiddenField){
16567             this.hiddenField.dom.value = '';
16568         }
16569         this.value = '';
16570         this.setRawValue('');
16571         this.lastSelectionText = '';
16572         this.lastData = false;
16573         
16574         var close = this.closeTriggerEl();
16575         
16576         if(close){
16577             close.hide();
16578         }
16579         
16580         this.validate();
16581         
16582     },
16583
16584     /**
16585      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16586      * will be displayed in the field.  If the value does not match the data value of an existing item,
16587      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16588      * Otherwise the field will be blank (although the value will still be set).
16589      * @param {String} value The value to match
16590      */
16591     setValue : function(v)
16592     {
16593         if(Roo.isIOS && this.useNativeIOS){
16594             this.setIOSValue(v);
16595             return;
16596         }
16597         
16598         if(this.multiple){
16599             this.syncValue();
16600             return;
16601         }
16602         
16603         var text = v;
16604         if(this.valueField){
16605             var r = this.findRecord(this.valueField, v);
16606             if(r){
16607                 text = r.data[this.displayField];
16608             }else if(this.valueNotFoundText !== undefined){
16609                 text = this.valueNotFoundText;
16610             }
16611         }
16612         this.lastSelectionText = text;
16613         if(this.hiddenField){
16614             this.hiddenField.dom.value = v;
16615         }
16616         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16617         this.value = v;
16618         
16619         var close = this.closeTriggerEl();
16620         
16621         if(close){
16622             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16623         }
16624         
16625         this.validate();
16626     },
16627     /**
16628      * @property {Object} the last set data for the element
16629      */
16630     
16631     lastData : false,
16632     /**
16633      * Sets the value of the field based on a object which is related to the record format for the store.
16634      * @param {Object} value the value to set as. or false on reset?
16635      */
16636     setFromData : function(o){
16637         
16638         if(this.multiple){
16639             this.addItem(o);
16640             return;
16641         }
16642             
16643         var dv = ''; // display value
16644         var vv = ''; // value value..
16645         this.lastData = o;
16646         if (this.displayField) {
16647             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16648         } else {
16649             // this is an error condition!!!
16650             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16651         }
16652         
16653         if(this.valueField){
16654             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16655         }
16656         
16657         var close = this.closeTriggerEl();
16658         
16659         if(close){
16660             if(dv.length || vv * 1 > 0){
16661                 close.show() ;
16662                 this.blockFocus=true;
16663             } else {
16664                 close.hide();
16665             }             
16666         }
16667         
16668         if(this.hiddenField){
16669             this.hiddenField.dom.value = vv;
16670             
16671             this.lastSelectionText = dv;
16672             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16673             this.value = vv;
16674             return;
16675         }
16676         // no hidden field.. - we store the value in 'value', but still display
16677         // display field!!!!
16678         this.lastSelectionText = dv;
16679         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16680         this.value = vv;
16681         
16682         
16683         
16684     },
16685     // private
16686     reset : function(){
16687         // overridden so that last data is reset..
16688         
16689         if(this.multiple){
16690             this.clearItem();
16691             return;
16692         }
16693         
16694         this.setValue(this.originalValue);
16695         //this.clearInvalid();
16696         this.lastData = false;
16697         if (this.view) {
16698             this.view.clearSelections();
16699         }
16700         
16701         this.validate();
16702     },
16703     // private
16704     findRecord : function(prop, value){
16705         var record;
16706         if(this.store.getCount() > 0){
16707             this.store.each(function(r){
16708                 if(r.data[prop] == value){
16709                     record = r;
16710                     return false;
16711                 }
16712                 return true;
16713             });
16714         }
16715         return record;
16716     },
16717     
16718     getName: function()
16719     {
16720         // returns hidden if it's set..
16721         if (!this.rendered) {return ''};
16722         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16723         
16724     },
16725     // private
16726     onViewMove : function(e, t){
16727         this.inKeyMode = false;
16728     },
16729
16730     // private
16731     onViewOver : function(e, t){
16732         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16733             return;
16734         }
16735         var item = this.view.findItemFromChild(t);
16736         
16737         if(item){
16738             var index = this.view.indexOf(item);
16739             this.select(index, false);
16740         }
16741     },
16742
16743     // private
16744     onViewClick : function(view, doFocus, el, e)
16745     {
16746         var index = this.view.getSelectedIndexes()[0];
16747         
16748         var r = this.store.getAt(index);
16749         
16750         if(this.tickable){
16751             
16752             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16753                 return;
16754             }
16755             
16756             var rm = false;
16757             var _this = this;
16758             
16759             Roo.each(this.tickItems, function(v,k){
16760                 
16761                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16762                     Roo.log(v);
16763                     _this.tickItems.splice(k, 1);
16764                     
16765                     if(typeof(e) == 'undefined' && view == false){
16766                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16767                     }
16768                     
16769                     rm = true;
16770                     return;
16771                 }
16772             });
16773             
16774             if(rm){
16775                 return;
16776             }
16777             
16778             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16779                 this.tickItems.push(r.data);
16780             }
16781             
16782             if(typeof(e) == 'undefined' && view == false){
16783                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16784             }
16785                     
16786             return;
16787         }
16788         
16789         if(r){
16790             this.onSelect(r, index);
16791         }
16792         if(doFocus !== false && !this.blockFocus){
16793             this.inputEl().focus();
16794         }
16795     },
16796
16797     // private
16798     restrictHeight : function(){
16799         //this.innerList.dom.style.height = '';
16800         //var inner = this.innerList.dom;
16801         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16802         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16803         //this.list.beginUpdate();
16804         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16805         this.list.alignTo(this.inputEl(), this.listAlign);
16806         this.list.alignTo(this.inputEl(), this.listAlign);
16807         //this.list.endUpdate();
16808     },
16809
16810     // private
16811     onEmptyResults : function(){
16812         
16813         if(this.tickable && this.editable){
16814             this.hasFocus = false;
16815             this.restrictHeight();
16816             return;
16817         }
16818         
16819         this.collapse();
16820     },
16821
16822     /**
16823      * Returns true if the dropdown list is expanded, else false.
16824      */
16825     isExpanded : function(){
16826         return this.list.isVisible();
16827     },
16828
16829     /**
16830      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16831      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16832      * @param {String} value The data value of the item to select
16833      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16834      * selected item if it is not currently in view (defaults to true)
16835      * @return {Boolean} True if the value matched an item in the list, else false
16836      */
16837     selectByValue : function(v, scrollIntoView){
16838         if(v !== undefined && v !== null){
16839             var r = this.findRecord(this.valueField || this.displayField, v);
16840             if(r){
16841                 this.select(this.store.indexOf(r), scrollIntoView);
16842                 return true;
16843             }
16844         }
16845         return false;
16846     },
16847
16848     /**
16849      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16850      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16851      * @param {Number} index The zero-based index of the list item to select
16852      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16853      * selected item if it is not currently in view (defaults to true)
16854      */
16855     select : function(index, scrollIntoView){
16856         this.selectedIndex = index;
16857         this.view.select(index);
16858         if(scrollIntoView !== false){
16859             var el = this.view.getNode(index);
16860             /*
16861              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16862              */
16863             if(el){
16864                 this.list.scrollChildIntoView(el, false);
16865             }
16866         }
16867     },
16868
16869     // private
16870     selectNext : function(){
16871         var ct = this.store.getCount();
16872         if(ct > 0){
16873             if(this.selectedIndex == -1){
16874                 this.select(0);
16875             }else if(this.selectedIndex < ct-1){
16876                 this.select(this.selectedIndex+1);
16877             }
16878         }
16879     },
16880
16881     // private
16882     selectPrev : function(){
16883         var ct = this.store.getCount();
16884         if(ct > 0){
16885             if(this.selectedIndex == -1){
16886                 this.select(0);
16887             }else if(this.selectedIndex != 0){
16888                 this.select(this.selectedIndex-1);
16889             }
16890         }
16891     },
16892
16893     // private
16894     onKeyUp : function(e){
16895         if(this.editable !== false && !e.isSpecialKey()){
16896             this.lastKey = e.getKey();
16897             this.dqTask.delay(this.queryDelay);
16898         }
16899     },
16900
16901     // private
16902     validateBlur : function(){
16903         return !this.list || !this.list.isVisible();   
16904     },
16905
16906     // private
16907     initQuery : function(){
16908         
16909         var v = this.getRawValue();
16910         
16911         if(this.tickable && this.editable){
16912             v = this.tickableInputEl().getValue();
16913         }
16914         
16915         this.doQuery(v);
16916     },
16917
16918     // private
16919     doForce : function(){
16920         if(this.inputEl().dom.value.length > 0){
16921             this.inputEl().dom.value =
16922                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16923              
16924         }
16925     },
16926
16927     /**
16928      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16929      * query allowing the query action to be canceled if needed.
16930      * @param {String} query The SQL query to execute
16931      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16932      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16933      * saved in the current store (defaults to false)
16934      */
16935     doQuery : function(q, forceAll){
16936         
16937         if(q === undefined || q === null){
16938             q = '';
16939         }
16940         var qe = {
16941             query: q,
16942             forceAll: forceAll,
16943             combo: this,
16944             cancel:false
16945         };
16946         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16947             return false;
16948         }
16949         q = qe.query;
16950         
16951         forceAll = qe.forceAll;
16952         if(forceAll === true || (q.length >= this.minChars)){
16953             
16954             this.hasQuery = true;
16955             
16956             if(this.lastQuery != q || this.alwaysQuery){
16957                 this.lastQuery = q;
16958                 if(this.mode == 'local'){
16959                     this.selectedIndex = -1;
16960                     if(forceAll){
16961                         this.store.clearFilter();
16962                     }else{
16963                         
16964                         if(this.specialFilter){
16965                             this.fireEvent('specialfilter', this);
16966                             this.onLoad();
16967                             return;
16968                         }
16969                         
16970                         this.store.filter(this.displayField, q);
16971                     }
16972                     
16973                     this.store.fireEvent("datachanged", this.store);
16974                     
16975                     this.onLoad();
16976                     
16977                     
16978                 }else{
16979                     
16980                     this.store.baseParams[this.queryParam] = q;
16981                     
16982                     var options = {params : this.getParams(q)};
16983                     
16984                     if(this.loadNext){
16985                         options.add = true;
16986                         options.params.start = this.page * this.pageSize;
16987                     }
16988                     
16989                     this.store.load(options);
16990                     
16991                     /*
16992                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16993                      *  we should expand the list on onLoad
16994                      *  so command out it
16995                      */
16996 //                    this.expand();
16997                 }
16998             }else{
16999                 this.selectedIndex = -1;
17000                 this.onLoad();   
17001             }
17002         }
17003         
17004         this.loadNext = false;
17005     },
17006     
17007     // private
17008     getParams : function(q){
17009         var p = {};
17010         //p[this.queryParam] = q;
17011         
17012         if(this.pageSize){
17013             p.start = 0;
17014             p.limit = this.pageSize;
17015         }
17016         return p;
17017     },
17018
17019     /**
17020      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17021      */
17022     collapse : function(){
17023         if(!this.isExpanded()){
17024             return;
17025         }
17026         
17027         this.list.hide();
17028         
17029         this.hasFocus = false;
17030         
17031         if(this.tickable){
17032             this.okBtn.hide();
17033             this.cancelBtn.hide();
17034             this.trigger.show();
17035             
17036             if(this.editable){
17037                 this.tickableInputEl().dom.value = '';
17038                 this.tickableInputEl().blur();
17039             }
17040             
17041         }
17042         
17043         Roo.get(document).un('mousedown', this.collapseIf, this);
17044         Roo.get(document).un('mousewheel', this.collapseIf, this);
17045         if (!this.editable) {
17046             Roo.get(document).un('keydown', this.listKeyPress, this);
17047         }
17048         this.fireEvent('collapse', this);
17049         
17050         this.validate();
17051     },
17052
17053     // private
17054     collapseIf : function(e){
17055         var in_combo  = e.within(this.el);
17056         var in_list =  e.within(this.list);
17057         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17058         
17059         if (in_combo || in_list || is_list) {
17060             //e.stopPropagation();
17061             return;
17062         }
17063         
17064         if(this.tickable){
17065             this.onTickableFooterButtonClick(e, false, false);
17066         }
17067
17068         this.collapse();
17069         
17070     },
17071
17072     /**
17073      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17074      */
17075     expand : function(){
17076        
17077         if(this.isExpanded() || !this.hasFocus){
17078             return;
17079         }
17080         
17081         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17082         this.list.setWidth(lw);
17083         
17084         Roo.log('expand');
17085         
17086         this.list.show();
17087         
17088         this.restrictHeight();
17089         
17090         if(this.tickable){
17091             
17092             this.tickItems = Roo.apply([], this.item);
17093             
17094             this.okBtn.show();
17095             this.cancelBtn.show();
17096             this.trigger.hide();
17097             
17098             if(this.editable){
17099                 this.tickableInputEl().focus();
17100             }
17101             
17102         }
17103         
17104         Roo.get(document).on('mousedown', this.collapseIf, this);
17105         Roo.get(document).on('mousewheel', this.collapseIf, this);
17106         if (!this.editable) {
17107             Roo.get(document).on('keydown', this.listKeyPress, this);
17108         }
17109         
17110         this.fireEvent('expand', this);
17111     },
17112
17113     // private
17114     // Implements the default empty TriggerField.onTriggerClick function
17115     onTriggerClick : function(e)
17116     {
17117         Roo.log('trigger click');
17118         
17119         if(this.disabled || !this.triggerList){
17120             return;
17121         }
17122         
17123         this.page = 0;
17124         this.loadNext = false;
17125         
17126         if(this.isExpanded()){
17127             this.collapse();
17128             if (!this.blockFocus) {
17129                 this.inputEl().focus();
17130             }
17131             
17132         }else {
17133             this.hasFocus = true;
17134             if(this.triggerAction == 'all') {
17135                 this.doQuery(this.allQuery, true);
17136             } else {
17137                 this.doQuery(this.getRawValue());
17138             }
17139             if (!this.blockFocus) {
17140                 this.inputEl().focus();
17141             }
17142         }
17143     },
17144     
17145     onTickableTriggerClick : function(e)
17146     {
17147         if(this.disabled){
17148             return;
17149         }
17150         
17151         this.page = 0;
17152         this.loadNext = false;
17153         this.hasFocus = true;
17154         
17155         if(this.triggerAction == 'all') {
17156             this.doQuery(this.allQuery, true);
17157         } else {
17158             this.doQuery(this.getRawValue());
17159         }
17160     },
17161     
17162     onSearchFieldClick : function(e)
17163     {
17164         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17165             this.onTickableFooterButtonClick(e, false, false);
17166             return;
17167         }
17168         
17169         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17170             return;
17171         }
17172         
17173         this.page = 0;
17174         this.loadNext = false;
17175         this.hasFocus = true;
17176         
17177         if(this.triggerAction == 'all') {
17178             this.doQuery(this.allQuery, true);
17179         } else {
17180             this.doQuery(this.getRawValue());
17181         }
17182     },
17183     
17184     listKeyPress : function(e)
17185     {
17186         //Roo.log('listkeypress');
17187         // scroll to first matching element based on key pres..
17188         if (e.isSpecialKey()) {
17189             return false;
17190         }
17191         var k = String.fromCharCode(e.getKey()).toUpperCase();
17192         //Roo.log(k);
17193         var match  = false;
17194         var csel = this.view.getSelectedNodes();
17195         var cselitem = false;
17196         if (csel.length) {
17197             var ix = this.view.indexOf(csel[0]);
17198             cselitem  = this.store.getAt(ix);
17199             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17200                 cselitem = false;
17201             }
17202             
17203         }
17204         
17205         this.store.each(function(v) { 
17206             if (cselitem) {
17207                 // start at existing selection.
17208                 if (cselitem.id == v.id) {
17209                     cselitem = false;
17210                 }
17211                 return true;
17212             }
17213                 
17214             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17215                 match = this.store.indexOf(v);
17216                 return false;
17217             }
17218             return true;
17219         }, this);
17220         
17221         if (match === false) {
17222             return true; // no more action?
17223         }
17224         // scroll to?
17225         this.view.select(match);
17226         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17227         sn.scrollIntoView(sn.dom.parentNode, false);
17228     },
17229     
17230     onViewScroll : function(e, t){
17231         
17232         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){
17233             return;
17234         }
17235         
17236         this.hasQuery = true;
17237         
17238         this.loading = this.list.select('.loading', true).first();
17239         
17240         if(this.loading === null){
17241             this.list.createChild({
17242                 tag: 'div',
17243                 cls: 'loading roo-select2-more-results roo-select2-active',
17244                 html: 'Loading more results...'
17245             });
17246             
17247             this.loading = this.list.select('.loading', true).first();
17248             
17249             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17250             
17251             this.loading.hide();
17252         }
17253         
17254         this.loading.show();
17255         
17256         var _combo = this;
17257         
17258         this.page++;
17259         this.loadNext = true;
17260         
17261         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17262         
17263         return;
17264     },
17265     
17266     addItem : function(o)
17267     {   
17268         var dv = ''; // display value
17269         
17270         if (this.displayField) {
17271             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17272         } else {
17273             // this is an error condition!!!
17274             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17275         }
17276         
17277         if(!dv.length){
17278             return;
17279         }
17280         
17281         var choice = this.choices.createChild({
17282             tag: 'li',
17283             cls: 'roo-select2-search-choice',
17284             cn: [
17285                 {
17286                     tag: 'div',
17287                     html: dv
17288                 },
17289                 {
17290                     tag: 'a',
17291                     href: '#',
17292                     cls: 'roo-select2-search-choice-close fa fa-times',
17293                     tabindex: '-1'
17294                 }
17295             ]
17296             
17297         }, this.searchField);
17298         
17299         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17300         
17301         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17302         
17303         this.item.push(o);
17304         
17305         this.lastData = o;
17306         
17307         this.syncValue();
17308         
17309         this.inputEl().dom.value = '';
17310         
17311         this.validate();
17312     },
17313     
17314     onRemoveItem : function(e, _self, o)
17315     {
17316         e.preventDefault();
17317         
17318         this.lastItem = Roo.apply([], this.item);
17319         
17320         var index = this.item.indexOf(o.data) * 1;
17321         
17322         if( index < 0){
17323             Roo.log('not this item?!');
17324             return;
17325         }
17326         
17327         this.item.splice(index, 1);
17328         o.item.remove();
17329         
17330         this.syncValue();
17331         
17332         this.fireEvent('remove', this, e);
17333         
17334         this.validate();
17335         
17336     },
17337     
17338     syncValue : function()
17339     {
17340         if(!this.item.length){
17341             this.clearValue();
17342             return;
17343         }
17344             
17345         var value = [];
17346         var _this = this;
17347         Roo.each(this.item, function(i){
17348             if(_this.valueField){
17349                 value.push(i[_this.valueField]);
17350                 return;
17351             }
17352
17353             value.push(i);
17354         });
17355
17356         this.value = value.join(',');
17357
17358         if(this.hiddenField){
17359             this.hiddenField.dom.value = this.value;
17360         }
17361         
17362         this.store.fireEvent("datachanged", this.store);
17363         
17364         this.validate();
17365     },
17366     
17367     clearItem : function()
17368     {
17369         if(!this.multiple){
17370             return;
17371         }
17372         
17373         this.item = [];
17374         
17375         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17376            c.remove();
17377         });
17378         
17379         this.syncValue();
17380         
17381         this.validate();
17382         
17383         if(this.tickable && !Roo.isTouch){
17384             this.view.refresh();
17385         }
17386     },
17387     
17388     inputEl: function ()
17389     {
17390         if(Roo.isIOS && this.useNativeIOS){
17391             return this.el.select('select.roo-ios-select', true).first();
17392         }
17393         
17394         if(Roo.isTouch && this.mobileTouchView){
17395             return this.el.select('input.form-control',true).first();
17396         }
17397         
17398         if(this.tickable){
17399             return this.searchField;
17400         }
17401         
17402         return this.el.select('input.form-control',true).first();
17403     },
17404     
17405     onTickableFooterButtonClick : function(e, btn, el)
17406     {
17407         e.preventDefault();
17408         
17409         this.lastItem = Roo.apply([], this.item);
17410         
17411         if(btn && btn.name == 'cancel'){
17412             this.tickItems = Roo.apply([], this.item);
17413             this.collapse();
17414             return;
17415         }
17416         
17417         this.clearItem();
17418         
17419         var _this = this;
17420         
17421         Roo.each(this.tickItems, function(o){
17422             _this.addItem(o);
17423         });
17424         
17425         this.collapse();
17426         
17427     },
17428     
17429     validate : function()
17430     {
17431         if(this.getVisibilityEl().hasClass('hidden')){
17432             return true;
17433         }
17434         
17435         var v = this.getRawValue();
17436         
17437         if(this.multiple){
17438             v = this.getValue();
17439         }
17440         
17441         if(this.disabled || this.allowBlank || v.length){
17442             this.markValid();
17443             return true;
17444         }
17445         
17446         this.markInvalid();
17447         return false;
17448     },
17449     
17450     tickableInputEl : function()
17451     {
17452         if(!this.tickable || !this.editable){
17453             return this.inputEl();
17454         }
17455         
17456         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17457     },
17458     
17459     
17460     getAutoCreateTouchView : function()
17461     {
17462         var id = Roo.id();
17463         
17464         var cfg = {
17465             cls: 'form-group' //input-group
17466         };
17467         
17468         var input =  {
17469             tag: 'input',
17470             id : id,
17471             type : this.inputType,
17472             cls : 'form-control x-combo-noedit',
17473             autocomplete: 'new-password',
17474             placeholder : this.placeholder || '',
17475             readonly : true
17476         };
17477         
17478         if (this.name) {
17479             input.name = this.name;
17480         }
17481         
17482         if (this.size) {
17483             input.cls += ' input-' + this.size;
17484         }
17485         
17486         if (this.disabled) {
17487             input.disabled = true;
17488         }
17489         
17490         var inputblock = {
17491             cls : 'roo-combobox-wrap',
17492             cn : [
17493                 input
17494             ]
17495         };
17496         
17497         if(this.before){
17498             inputblock.cls += ' input-group';
17499             
17500             inputblock.cn.unshift({
17501                 tag :'span',
17502                 cls : 'input-group-addon input-group-prepend input-group-text',
17503                 html : this.before
17504             });
17505         }
17506         
17507         if(this.removable && !this.multiple){
17508             inputblock.cls += ' roo-removable';
17509             
17510             inputblock.cn.push({
17511                 tag: 'button',
17512                 html : 'x',
17513                 cls : 'roo-combo-removable-btn close'
17514             });
17515         }
17516
17517         if(this.hasFeedback && !this.allowBlank){
17518             
17519             inputblock.cls += ' has-feedback';
17520             
17521             inputblock.cn.push({
17522                 tag: 'span',
17523                 cls: 'glyphicon form-control-feedback'
17524             });
17525             
17526         }
17527         
17528         if (this.after) {
17529             
17530             inputblock.cls += (this.before) ? '' : ' input-group';
17531             
17532             inputblock.cn.push({
17533                 tag :'span',
17534                 cls : 'input-group-addon input-group-append input-group-text',
17535                 html : this.after
17536             });
17537         }
17538
17539         
17540         var ibwrap = inputblock;
17541         
17542         if(this.multiple){
17543             ibwrap = {
17544                 tag: 'ul',
17545                 cls: 'roo-select2-choices',
17546                 cn:[
17547                     {
17548                         tag: 'li',
17549                         cls: 'roo-select2-search-field',
17550                         cn: [
17551
17552                             inputblock
17553                         ]
17554                     }
17555                 ]
17556             };
17557         
17558             
17559         }
17560         
17561         var combobox = {
17562             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17563             cn: [
17564                 {
17565                     tag: 'input',
17566                     type : 'hidden',
17567                     cls: 'form-hidden-field'
17568                 },
17569                 ibwrap
17570             ]
17571         };
17572         
17573         if(!this.multiple && this.showToggleBtn){
17574             
17575             var caret = {
17576                 cls: 'caret'
17577             };
17578             
17579             if (this.caret != false) {
17580                 caret = {
17581                      tag: 'i',
17582                      cls: 'fa fa-' + this.caret
17583                 };
17584                 
17585             }
17586             
17587             combobox.cn.push({
17588                 tag :'span',
17589                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17590                 cn : [
17591                     Roo.bootstrap.version == 3 ? caret : '',
17592                     {
17593                         tag: 'span',
17594                         cls: 'combobox-clear',
17595                         cn  : [
17596                             {
17597                                 tag : 'i',
17598                                 cls: 'icon-remove'
17599                             }
17600                         ]
17601                     }
17602                 ]
17603
17604             })
17605         }
17606         
17607         if(this.multiple){
17608             combobox.cls += ' roo-select2-container-multi';
17609         }
17610         
17611         var required =  this.allowBlank ?  {
17612                     tag : 'i',
17613                     style: 'display: none'
17614                 } : {
17615                    tag : 'i',
17616                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17617                    tooltip : 'This field is required'
17618                 };
17619         
17620         var align = this.labelAlign || this.parentLabelAlign();
17621         
17622         if (align ==='left' && this.fieldLabel.length) {
17623
17624             cfg.cn = [
17625                 required,
17626                 {
17627                     tag: 'label',
17628                     cls : 'control-label col-form-label',
17629                     html : this.fieldLabel
17630
17631                 },
17632                 {
17633                     cls : 'roo-combobox-wrap ', 
17634                     cn: [
17635                         combobox
17636                     ]
17637                 }
17638             ];
17639             
17640             var labelCfg = cfg.cn[1];
17641             var contentCfg = cfg.cn[2];
17642             
17643
17644             if(this.indicatorpos == 'right'){
17645                 cfg.cn = [
17646                     {
17647                         tag: 'label',
17648                         'for' :  id,
17649                         cls : 'control-label col-form-label',
17650                         cn : [
17651                             {
17652                                 tag : 'span',
17653                                 html : this.fieldLabel
17654                             },
17655                             required
17656                         ]
17657                     },
17658                     {
17659                         cls : "roo-combobox-wrap ",
17660                         cn: [
17661                             combobox
17662                         ]
17663                     }
17664
17665                 ];
17666                 
17667                 labelCfg = cfg.cn[0];
17668                 contentCfg = cfg.cn[1];
17669             }
17670             
17671            
17672             
17673             if(this.labelWidth > 12){
17674                 labelCfg.style = "width: " + this.labelWidth + 'px';
17675             }
17676            
17677             if(this.labelWidth < 13 && this.labelmd == 0){
17678                 this.labelmd = this.labelWidth;
17679             }
17680             
17681             if(this.labellg > 0){
17682                 labelCfg.cls += ' col-lg-' + this.labellg;
17683                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17684             }
17685             
17686             if(this.labelmd > 0){
17687                 labelCfg.cls += ' col-md-' + this.labelmd;
17688                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17689             }
17690             
17691             if(this.labelsm > 0){
17692                 labelCfg.cls += ' col-sm-' + this.labelsm;
17693                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17694             }
17695             
17696             if(this.labelxs > 0){
17697                 labelCfg.cls += ' col-xs-' + this.labelxs;
17698                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17699             }
17700                 
17701                 
17702         } else if ( this.fieldLabel.length) {
17703             cfg.cn = [
17704                required,
17705                 {
17706                     tag: 'label',
17707                     cls : 'control-label',
17708                     html : this.fieldLabel
17709
17710                 },
17711                 {
17712                     cls : '', 
17713                     cn: [
17714                         combobox
17715                     ]
17716                 }
17717             ];
17718             
17719             if(this.indicatorpos == 'right'){
17720                 cfg.cn = [
17721                     {
17722                         tag: 'label',
17723                         cls : 'control-label',
17724                         html : this.fieldLabel,
17725                         cn : [
17726                             required
17727                         ]
17728                     },
17729                     {
17730                         cls : '', 
17731                         cn: [
17732                             combobox
17733                         ]
17734                     }
17735                 ];
17736             }
17737         } else {
17738             cfg.cn = combobox;    
17739         }
17740         
17741         
17742         var settings = this;
17743         
17744         ['xs','sm','md','lg'].map(function(size){
17745             if (settings[size]) {
17746                 cfg.cls += ' col-' + size + '-' + settings[size];
17747             }
17748         });
17749         
17750         return cfg;
17751     },
17752     
17753     initTouchView : function()
17754     {
17755         this.renderTouchView();
17756         
17757         this.touchViewEl.on('scroll', function(){
17758             this.el.dom.scrollTop = 0;
17759         }, this);
17760         
17761         this.originalValue = this.getValue();
17762         
17763         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17764         
17765         this.inputEl().on("click", this.showTouchView, this);
17766         if (this.triggerEl) {
17767             this.triggerEl.on("click", this.showTouchView, this);
17768         }
17769         
17770         
17771         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17772         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17773         
17774         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17775         
17776         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17777         this.store.on('load', this.onTouchViewLoad, this);
17778         this.store.on('loadexception', this.onTouchViewLoadException, this);
17779         
17780         if(this.hiddenName){
17781             
17782             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17783             
17784             this.hiddenField.dom.value =
17785                 this.hiddenValue !== undefined ? this.hiddenValue :
17786                 this.value !== undefined ? this.value : '';
17787         
17788             this.el.dom.removeAttribute('name');
17789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17790         }
17791         
17792         if(this.multiple){
17793             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17794             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17795         }
17796         
17797         if(this.removable && !this.multiple){
17798             var close = this.closeTriggerEl();
17799             if(close){
17800                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17801                 close.on('click', this.removeBtnClick, this, close);
17802             }
17803         }
17804         /*
17805          * fix the bug in Safari iOS8
17806          */
17807         this.inputEl().on("focus", function(e){
17808             document.activeElement.blur();
17809         }, this);
17810         
17811         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17812         
17813         return;
17814         
17815         
17816     },
17817     
17818     renderTouchView : function()
17819     {
17820         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17821         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17822         
17823         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17824         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17825         
17826         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17827         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17828         this.touchViewBodyEl.setStyle('overflow', 'auto');
17829         
17830         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17831         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17832         
17833         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17834         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17835         
17836     },
17837     
17838     showTouchView : function()
17839     {
17840         if(this.disabled){
17841             return;
17842         }
17843         
17844         this.touchViewHeaderEl.hide();
17845
17846         if(this.modalTitle.length){
17847             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17848             this.touchViewHeaderEl.show();
17849         }
17850
17851         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17852         this.touchViewEl.show();
17853
17854         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17855         
17856         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17857         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17858
17859         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17860
17861         if(this.modalTitle.length){
17862             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17863         }
17864         
17865         this.touchViewBodyEl.setHeight(bodyHeight);
17866
17867         if(this.animate){
17868             var _this = this;
17869             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17870         }else{
17871             this.touchViewEl.addClass(['in','show']);
17872         }
17873         
17874         if(this._touchViewMask){
17875             Roo.get(document.body).addClass("x-body-masked");
17876             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17877             this._touchViewMask.setStyle('z-index', 10000);
17878             this._touchViewMask.addClass('show');
17879         }
17880         
17881         this.doTouchViewQuery();
17882         
17883     },
17884     
17885     hideTouchView : function()
17886     {
17887         this.touchViewEl.removeClass(['in','show']);
17888
17889         if(this.animate){
17890             var _this = this;
17891             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17892         }else{
17893             this.touchViewEl.setStyle('display', 'none');
17894         }
17895         
17896         if(this._touchViewMask){
17897             this._touchViewMask.removeClass('show');
17898             Roo.get(document.body).removeClass("x-body-masked");
17899         }
17900     },
17901     
17902     setTouchViewValue : function()
17903     {
17904         if(this.multiple){
17905             this.clearItem();
17906         
17907             var _this = this;
17908
17909             Roo.each(this.tickItems, function(o){
17910                 this.addItem(o);
17911             }, this);
17912         }
17913         
17914         this.hideTouchView();
17915     },
17916     
17917     doTouchViewQuery : function()
17918     {
17919         var qe = {
17920             query: '',
17921             forceAll: true,
17922             combo: this,
17923             cancel:false
17924         };
17925         
17926         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17927             return false;
17928         }
17929         
17930         if(!this.alwaysQuery || this.mode == 'local'){
17931             this.onTouchViewLoad();
17932             return;
17933         }
17934         
17935         this.store.load();
17936     },
17937     
17938     onTouchViewBeforeLoad : function(combo,opts)
17939     {
17940         return;
17941     },
17942
17943     // private
17944     onTouchViewLoad : function()
17945     {
17946         if(this.store.getCount() < 1){
17947             this.onTouchViewEmptyResults();
17948             return;
17949         }
17950         
17951         this.clearTouchView();
17952         
17953         var rawValue = this.getRawValue();
17954         
17955         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17956         
17957         this.tickItems = [];
17958         
17959         this.store.data.each(function(d, rowIndex){
17960             var row = this.touchViewListGroup.createChild(template);
17961             
17962             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17963                 row.addClass(d.data.cls);
17964             }
17965             
17966             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17967                 var cfg = {
17968                     data : d.data,
17969                     html : d.data[this.displayField]
17970                 };
17971                 
17972                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17973                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17974                 }
17975             }
17976             row.removeClass('selected');
17977             if(!this.multiple && this.valueField &&
17978                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17979             {
17980                 // radio buttons..
17981                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17982                 row.addClass('selected');
17983             }
17984             
17985             if(this.multiple && this.valueField &&
17986                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17987             {
17988                 
17989                 // checkboxes...
17990                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17991                 this.tickItems.push(d.data);
17992             }
17993             
17994             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17995             
17996         }, this);
17997         
17998         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17999         
18000         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18001
18002         if(this.modalTitle.length){
18003             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18004         }
18005
18006         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18007         
18008         if(this.mobile_restrict_height && listHeight < bodyHeight){
18009             this.touchViewBodyEl.setHeight(listHeight);
18010         }
18011         
18012         var _this = this;
18013         
18014         if(firstChecked && listHeight > bodyHeight){
18015             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18016         }
18017         
18018     },
18019     
18020     onTouchViewLoadException : function()
18021     {
18022         this.hideTouchView();
18023     },
18024     
18025     onTouchViewEmptyResults : function()
18026     {
18027         this.clearTouchView();
18028         
18029         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18030         
18031         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18032         
18033     },
18034     
18035     clearTouchView : function()
18036     {
18037         this.touchViewListGroup.dom.innerHTML = '';
18038     },
18039     
18040     onTouchViewClick : function(e, el, o)
18041     {
18042         e.preventDefault();
18043         
18044         var row = o.row;
18045         var rowIndex = o.rowIndex;
18046         
18047         var r = this.store.getAt(rowIndex);
18048         
18049         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18050             
18051             if(!this.multiple){
18052                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18053                     c.dom.removeAttribute('checked');
18054                 }, this);
18055
18056                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18057
18058                 this.setFromData(r.data);
18059
18060                 var close = this.closeTriggerEl();
18061
18062                 if(close){
18063                     close.show();
18064                 }
18065
18066                 this.hideTouchView();
18067
18068                 this.fireEvent('select', this, r, rowIndex);
18069
18070                 return;
18071             }
18072
18073             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18074                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18075                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18076                 return;
18077             }
18078
18079             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18080             this.addItem(r.data);
18081             this.tickItems.push(r.data);
18082         }
18083     },
18084     
18085     getAutoCreateNativeIOS : function()
18086     {
18087         var cfg = {
18088             cls: 'form-group' //input-group,
18089         };
18090         
18091         var combobox =  {
18092             tag: 'select',
18093             cls : 'roo-ios-select'
18094         };
18095         
18096         if (this.name) {
18097             combobox.name = this.name;
18098         }
18099         
18100         if (this.disabled) {
18101             combobox.disabled = true;
18102         }
18103         
18104         var settings = this;
18105         
18106         ['xs','sm','md','lg'].map(function(size){
18107             if (settings[size]) {
18108                 cfg.cls += ' col-' + size + '-' + settings[size];
18109             }
18110         });
18111         
18112         cfg.cn = combobox;
18113         
18114         return cfg;
18115         
18116     },
18117     
18118     initIOSView : function()
18119     {
18120         this.store.on('load', this.onIOSViewLoad, this);
18121         
18122         return;
18123     },
18124     
18125     onIOSViewLoad : function()
18126     {
18127         if(this.store.getCount() < 1){
18128             return;
18129         }
18130         
18131         this.clearIOSView();
18132         
18133         if(this.allowBlank) {
18134             
18135             var default_text = '-- SELECT --';
18136             
18137             if(this.placeholder.length){
18138                 default_text = this.placeholder;
18139             }
18140             
18141             if(this.emptyTitle.length){
18142                 default_text += ' - ' + this.emptyTitle + ' -';
18143             }
18144             
18145             var opt = this.inputEl().createChild({
18146                 tag: 'option',
18147                 value : 0,
18148                 html : default_text
18149             });
18150             
18151             var o = {};
18152             o[this.valueField] = 0;
18153             o[this.displayField] = default_text;
18154             
18155             this.ios_options.push({
18156                 data : o,
18157                 el : opt
18158             });
18159             
18160         }
18161         
18162         this.store.data.each(function(d, rowIndex){
18163             
18164             var html = '';
18165             
18166             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18167                 html = d.data[this.displayField];
18168             }
18169             
18170             var value = '';
18171             
18172             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18173                 value = d.data[this.valueField];
18174             }
18175             
18176             var option = {
18177                 tag: 'option',
18178                 value : value,
18179                 html : html
18180             };
18181             
18182             if(this.value == d.data[this.valueField]){
18183                 option['selected'] = true;
18184             }
18185             
18186             var opt = this.inputEl().createChild(option);
18187             
18188             this.ios_options.push({
18189                 data : d.data,
18190                 el : opt
18191             });
18192             
18193         }, this);
18194         
18195         this.inputEl().on('change', function(){
18196            this.fireEvent('select', this);
18197         }, this);
18198         
18199     },
18200     
18201     clearIOSView: function()
18202     {
18203         this.inputEl().dom.innerHTML = '';
18204         
18205         this.ios_options = [];
18206     },
18207     
18208     setIOSValue: function(v)
18209     {
18210         this.value = v;
18211         
18212         if(!this.ios_options){
18213             return;
18214         }
18215         
18216         Roo.each(this.ios_options, function(opts){
18217            
18218            opts.el.dom.removeAttribute('selected');
18219            
18220            if(opts.data[this.valueField] != v){
18221                return;
18222            }
18223            
18224            opts.el.dom.setAttribute('selected', true);
18225            
18226         }, this);
18227     }
18228
18229     /** 
18230     * @cfg {Boolean} grow 
18231     * @hide 
18232     */
18233     /** 
18234     * @cfg {Number} growMin 
18235     * @hide 
18236     */
18237     /** 
18238     * @cfg {Number} growMax 
18239     * @hide 
18240     */
18241     /**
18242      * @hide
18243      * @method autoSize
18244      */
18245 });
18246
18247 Roo.apply(Roo.bootstrap.ComboBox,  {
18248     
18249     header : {
18250         tag: 'div',
18251         cls: 'modal-header',
18252         cn: [
18253             {
18254                 tag: 'h4',
18255                 cls: 'modal-title'
18256             }
18257         ]
18258     },
18259     
18260     body : {
18261         tag: 'div',
18262         cls: 'modal-body',
18263         cn: [
18264             {
18265                 tag: 'ul',
18266                 cls: 'list-group'
18267             }
18268         ]
18269     },
18270     
18271     listItemRadio : {
18272         tag: 'li',
18273         cls: 'list-group-item',
18274         cn: [
18275             {
18276                 tag: 'span',
18277                 cls: 'roo-combobox-list-group-item-value'
18278             },
18279             {
18280                 tag: 'div',
18281                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18282                 cn: [
18283                     {
18284                         tag: 'input',
18285                         type: 'radio'
18286                     },
18287                     {
18288                         tag: 'label'
18289                     }
18290                 ]
18291             }
18292         ]
18293     },
18294     
18295     listItemCheckbox : {
18296         tag: 'li',
18297         cls: 'list-group-item',
18298         cn: [
18299             {
18300                 tag: 'span',
18301                 cls: 'roo-combobox-list-group-item-value'
18302             },
18303             {
18304                 tag: 'div',
18305                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18306                 cn: [
18307                     {
18308                         tag: 'input',
18309                         type: 'checkbox'
18310                     },
18311                     {
18312                         tag: 'label'
18313                     }
18314                 ]
18315             }
18316         ]
18317     },
18318     
18319     emptyResult : {
18320         tag: 'div',
18321         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18322     },
18323     
18324     footer : {
18325         tag: 'div',
18326         cls: 'modal-footer',
18327         cn: [
18328             {
18329                 tag: 'div',
18330                 cls: 'row',
18331                 cn: [
18332                     {
18333                         tag: 'div',
18334                         cls: 'col-xs-6 text-left',
18335                         cn: {
18336                             tag: 'button',
18337                             cls: 'btn btn-danger roo-touch-view-cancel',
18338                             html: 'Cancel'
18339                         }
18340                     },
18341                     {
18342                         tag: 'div',
18343                         cls: 'col-xs-6 text-right',
18344                         cn: {
18345                             tag: 'button',
18346                             cls: 'btn btn-success roo-touch-view-ok',
18347                             html: 'OK'
18348                         }
18349                     }
18350                 ]
18351             }
18352         ]
18353         
18354     }
18355 });
18356
18357 Roo.apply(Roo.bootstrap.ComboBox,  {
18358     
18359     touchViewTemplate : {
18360         tag: 'div',
18361         cls: 'modal fade roo-combobox-touch-view',
18362         cn: [
18363             {
18364                 tag: 'div',
18365                 cls: 'modal-dialog',
18366                 style : 'position:fixed', // we have to fix position....
18367                 cn: [
18368                     {
18369                         tag: 'div',
18370                         cls: 'modal-content',
18371                         cn: [
18372                             Roo.bootstrap.ComboBox.header,
18373                             Roo.bootstrap.ComboBox.body,
18374                             Roo.bootstrap.ComboBox.footer
18375                         ]
18376                     }
18377                 ]
18378             }
18379         ]
18380     }
18381 });/*
18382  * Based on:
18383  * Ext JS Library 1.1.1
18384  * Copyright(c) 2006-2007, Ext JS, LLC.
18385  *
18386  * Originally Released Under LGPL - original licence link has changed is not relivant.
18387  *
18388  * Fork - LGPL
18389  * <script type="text/javascript">
18390  */
18391
18392 /**
18393  * @class Roo.View
18394  * @extends Roo.util.Observable
18395  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18396  * This class also supports single and multi selection modes. <br>
18397  * Create a data model bound view:
18398  <pre><code>
18399  var store = new Roo.data.Store(...);
18400
18401  var view = new Roo.View({
18402     el : "my-element",
18403     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18404  
18405     singleSelect: true,
18406     selectedClass: "ydataview-selected",
18407     store: store
18408  });
18409
18410  // listen for node click?
18411  view.on("click", function(vw, index, node, e){
18412  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18413  });
18414
18415  // load XML data
18416  dataModel.load("foobar.xml");
18417  </code></pre>
18418  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18419  * <br><br>
18420  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18421  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18422  * 
18423  * Note: old style constructor is still suported (container, template, config)
18424  * 
18425  * @constructor
18426  * Create a new View
18427  * @param {Object} config The config object
18428  * 
18429  */
18430 Roo.View = function(config, depreciated_tpl, depreciated_config){
18431     
18432     this.parent = false;
18433     
18434     if (typeof(depreciated_tpl) == 'undefined') {
18435         // new way.. - universal constructor.
18436         Roo.apply(this, config);
18437         this.el  = Roo.get(this.el);
18438     } else {
18439         // old format..
18440         this.el  = Roo.get(config);
18441         this.tpl = depreciated_tpl;
18442         Roo.apply(this, depreciated_config);
18443     }
18444     this.wrapEl  = this.el.wrap().wrap();
18445     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18446     
18447     
18448     if(typeof(this.tpl) == "string"){
18449         this.tpl = new Roo.Template(this.tpl);
18450     } else {
18451         // support xtype ctors..
18452         this.tpl = new Roo.factory(this.tpl, Roo);
18453     }
18454     
18455     
18456     this.tpl.compile();
18457     
18458     /** @private */
18459     this.addEvents({
18460         /**
18461          * @event beforeclick
18462          * Fires before a click is processed. Returns false to cancel the default action.
18463          * @param {Roo.View} this
18464          * @param {Number} index The index of the target node
18465          * @param {HTMLElement} node The target node
18466          * @param {Roo.EventObject} e The raw event object
18467          */
18468             "beforeclick" : true,
18469         /**
18470          * @event click
18471          * Fires when a template node is clicked.
18472          * @param {Roo.View} this
18473          * @param {Number} index The index of the target node
18474          * @param {HTMLElement} node The target node
18475          * @param {Roo.EventObject} e The raw event object
18476          */
18477             "click" : true,
18478         /**
18479          * @event dblclick
18480          * Fires when a template node is double clicked.
18481          * @param {Roo.View} this
18482          * @param {Number} index The index of the target node
18483          * @param {HTMLElement} node The target node
18484          * @param {Roo.EventObject} e The raw event object
18485          */
18486             "dblclick" : true,
18487         /**
18488          * @event contextmenu
18489          * Fires when a template node is right clicked.
18490          * @param {Roo.View} this
18491          * @param {Number} index The index of the target node
18492          * @param {HTMLElement} node The target node
18493          * @param {Roo.EventObject} e The raw event object
18494          */
18495             "contextmenu" : true,
18496         /**
18497          * @event selectionchange
18498          * Fires when the selected nodes change.
18499          * @param {Roo.View} this
18500          * @param {Array} selections Array of the selected nodes
18501          */
18502             "selectionchange" : true,
18503     
18504         /**
18505          * @event beforeselect
18506          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18507          * @param {Roo.View} this
18508          * @param {HTMLElement} node The node to be selected
18509          * @param {Array} selections Array of currently selected nodes
18510          */
18511             "beforeselect" : true,
18512         /**
18513          * @event preparedata
18514          * Fires on every row to render, to allow you to change the data.
18515          * @param {Roo.View} this
18516          * @param {Object} data to be rendered (change this)
18517          */
18518           "preparedata" : true
18519           
18520           
18521         });
18522
18523
18524
18525     this.el.on({
18526         "click": this.onClick,
18527         "dblclick": this.onDblClick,
18528         "contextmenu": this.onContextMenu,
18529         scope:this
18530     });
18531
18532     this.selections = [];
18533     this.nodes = [];
18534     this.cmp = new Roo.CompositeElementLite([]);
18535     if(this.store){
18536         this.store = Roo.factory(this.store, Roo.data);
18537         this.setStore(this.store, true);
18538     }
18539     
18540     if ( this.footer && this.footer.xtype) {
18541            
18542          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18543         
18544         this.footer.dataSource = this.store;
18545         this.footer.container = fctr;
18546         this.footer = Roo.factory(this.footer, Roo);
18547         fctr.insertFirst(this.el);
18548         
18549         // this is a bit insane - as the paging toolbar seems to detach the el..
18550 //        dom.parentNode.parentNode.parentNode
18551          // they get detached?
18552     }
18553     
18554     
18555     Roo.View.superclass.constructor.call(this);
18556     
18557     
18558 };
18559
18560 Roo.extend(Roo.View, Roo.util.Observable, {
18561     
18562      /**
18563      * @cfg {Roo.data.Store} store Data store to load data from.
18564      */
18565     store : false,
18566     
18567     /**
18568      * @cfg {String|Roo.Element} el The container element.
18569      */
18570     el : '',
18571     
18572     /**
18573      * @cfg {String|Roo.Template} tpl The template used by this View 
18574      */
18575     tpl : false,
18576     /**
18577      * @cfg {String} dataName the named area of the template to use as the data area
18578      *                          Works with domtemplates roo-name="name"
18579      */
18580     dataName: false,
18581     /**
18582      * @cfg {String} selectedClass The css class to add to selected nodes
18583      */
18584     selectedClass : "x-view-selected",
18585      /**
18586      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18587      */
18588     emptyText : "",
18589     
18590     /**
18591      * @cfg {String} text to display on mask (default Loading)
18592      */
18593     mask : false,
18594     /**
18595      * @cfg {Boolean} multiSelect Allow multiple selection
18596      */
18597     multiSelect : false,
18598     /**
18599      * @cfg {Boolean} singleSelect Allow single selection
18600      */
18601     singleSelect:  false,
18602     
18603     /**
18604      * @cfg {Boolean} toggleSelect - selecting 
18605      */
18606     toggleSelect : false,
18607     
18608     /**
18609      * @cfg {Boolean} tickable - selecting 
18610      */
18611     tickable : false,
18612     
18613     /**
18614      * Returns the element this view is bound to.
18615      * @return {Roo.Element}
18616      */
18617     getEl : function(){
18618         return this.wrapEl;
18619     },
18620     
18621     
18622
18623     /**
18624      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18625      */
18626     refresh : function(){
18627         //Roo.log('refresh');
18628         var t = this.tpl;
18629         
18630         // if we are using something like 'domtemplate', then
18631         // the what gets used is:
18632         // t.applySubtemplate(NAME, data, wrapping data..)
18633         // the outer template then get' applied with
18634         //     the store 'extra data'
18635         // and the body get's added to the
18636         //      roo-name="data" node?
18637         //      <span class='roo-tpl-{name}'></span> ?????
18638         
18639         
18640         
18641         this.clearSelections();
18642         this.el.update("");
18643         var html = [];
18644         var records = this.store.getRange();
18645         if(records.length < 1) {
18646             
18647             // is this valid??  = should it render a template??
18648             
18649             this.el.update(this.emptyText);
18650             return;
18651         }
18652         var el = this.el;
18653         if (this.dataName) {
18654             this.el.update(t.apply(this.store.meta)); //????
18655             el = this.el.child('.roo-tpl-' + this.dataName);
18656         }
18657         
18658         for(var i = 0, len = records.length; i < len; i++){
18659             var data = this.prepareData(records[i].data, i, records[i]);
18660             this.fireEvent("preparedata", this, data, i, records[i]);
18661             
18662             var d = Roo.apply({}, data);
18663             
18664             if(this.tickable){
18665                 Roo.apply(d, {'roo-id' : Roo.id()});
18666                 
18667                 var _this = this;
18668             
18669                 Roo.each(this.parent.item, function(item){
18670                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18671                         return;
18672                     }
18673                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18674                 });
18675             }
18676             
18677             html[html.length] = Roo.util.Format.trim(
18678                 this.dataName ?
18679                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18680                     t.apply(d)
18681             );
18682         }
18683         
18684         
18685         
18686         el.update(html.join(""));
18687         this.nodes = el.dom.childNodes;
18688         this.updateIndexes(0);
18689     },
18690     
18691
18692     /**
18693      * Function to override to reformat the data that is sent to
18694      * the template for each node.
18695      * DEPRICATED - use the preparedata event handler.
18696      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18697      * a JSON object for an UpdateManager bound view).
18698      */
18699     prepareData : function(data, index, record)
18700     {
18701         this.fireEvent("preparedata", this, data, index, record);
18702         return data;
18703     },
18704
18705     onUpdate : function(ds, record){
18706         // Roo.log('on update');   
18707         this.clearSelections();
18708         var index = this.store.indexOf(record);
18709         var n = this.nodes[index];
18710         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18711         n.parentNode.removeChild(n);
18712         this.updateIndexes(index, index);
18713     },
18714
18715     
18716     
18717 // --------- FIXME     
18718     onAdd : function(ds, records, index)
18719     {
18720         //Roo.log(['on Add', ds, records, index] );        
18721         this.clearSelections();
18722         if(this.nodes.length == 0){
18723             this.refresh();
18724             return;
18725         }
18726         var n = this.nodes[index];
18727         for(var i = 0, len = records.length; i < len; i++){
18728             var d = this.prepareData(records[i].data, i, records[i]);
18729             if(n){
18730                 this.tpl.insertBefore(n, d);
18731             }else{
18732                 
18733                 this.tpl.append(this.el, d);
18734             }
18735         }
18736         this.updateIndexes(index);
18737     },
18738
18739     onRemove : function(ds, record, index){
18740        // Roo.log('onRemove');
18741         this.clearSelections();
18742         var el = this.dataName  ?
18743             this.el.child('.roo-tpl-' + this.dataName) :
18744             this.el; 
18745         
18746         el.dom.removeChild(this.nodes[index]);
18747         this.updateIndexes(index);
18748     },
18749
18750     /**
18751      * Refresh an individual node.
18752      * @param {Number} index
18753      */
18754     refreshNode : function(index){
18755         this.onUpdate(this.store, this.store.getAt(index));
18756     },
18757
18758     updateIndexes : function(startIndex, endIndex){
18759         var ns = this.nodes;
18760         startIndex = startIndex || 0;
18761         endIndex = endIndex || ns.length - 1;
18762         for(var i = startIndex; i <= endIndex; i++){
18763             ns[i].nodeIndex = i;
18764         }
18765     },
18766
18767     /**
18768      * Changes the data store this view uses and refresh the view.
18769      * @param {Store} store
18770      */
18771     setStore : function(store, initial){
18772         if(!initial && this.store){
18773             this.store.un("datachanged", this.refresh);
18774             this.store.un("add", this.onAdd);
18775             this.store.un("remove", this.onRemove);
18776             this.store.un("update", this.onUpdate);
18777             this.store.un("clear", this.refresh);
18778             this.store.un("beforeload", this.onBeforeLoad);
18779             this.store.un("load", this.onLoad);
18780             this.store.un("loadexception", this.onLoad);
18781         }
18782         if(store){
18783           
18784             store.on("datachanged", this.refresh, this);
18785             store.on("add", this.onAdd, this);
18786             store.on("remove", this.onRemove, this);
18787             store.on("update", this.onUpdate, this);
18788             store.on("clear", this.refresh, this);
18789             store.on("beforeload", this.onBeforeLoad, this);
18790             store.on("load", this.onLoad, this);
18791             store.on("loadexception", this.onLoad, this);
18792         }
18793         
18794         if(store){
18795             this.refresh();
18796         }
18797     },
18798     /**
18799      * onbeforeLoad - masks the loading area.
18800      *
18801      */
18802     onBeforeLoad : function(store,opts)
18803     {
18804          //Roo.log('onBeforeLoad');   
18805         if (!opts.add) {
18806             this.el.update("");
18807         }
18808         this.el.mask(this.mask ? this.mask : "Loading" ); 
18809     },
18810     onLoad : function ()
18811     {
18812         this.el.unmask();
18813     },
18814     
18815
18816     /**
18817      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18818      * @param {HTMLElement} node
18819      * @return {HTMLElement} The template node
18820      */
18821     findItemFromChild : function(node){
18822         var el = this.dataName  ?
18823             this.el.child('.roo-tpl-' + this.dataName,true) :
18824             this.el.dom; 
18825         
18826         if(!node || node.parentNode == el){
18827                     return node;
18828             }
18829             var p = node.parentNode;
18830             while(p && p != el){
18831             if(p.parentNode == el){
18832                 return p;
18833             }
18834             p = p.parentNode;
18835         }
18836             return null;
18837     },
18838
18839     /** @ignore */
18840     onClick : function(e){
18841         var item = this.findItemFromChild(e.getTarget());
18842         if(item){
18843             var index = this.indexOf(item);
18844             if(this.onItemClick(item, index, e) !== false){
18845                 this.fireEvent("click", this, index, item, e);
18846             }
18847         }else{
18848             this.clearSelections();
18849         }
18850     },
18851
18852     /** @ignore */
18853     onContextMenu : function(e){
18854         var item = this.findItemFromChild(e.getTarget());
18855         if(item){
18856             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18857         }
18858     },
18859
18860     /** @ignore */
18861     onDblClick : function(e){
18862         var item = this.findItemFromChild(e.getTarget());
18863         if(item){
18864             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18865         }
18866     },
18867
18868     onItemClick : function(item, index, e)
18869     {
18870         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18871             return false;
18872         }
18873         if (this.toggleSelect) {
18874             var m = this.isSelected(item) ? 'unselect' : 'select';
18875             //Roo.log(m);
18876             var _t = this;
18877             _t[m](item, true, false);
18878             return true;
18879         }
18880         if(this.multiSelect || this.singleSelect){
18881             if(this.multiSelect && e.shiftKey && this.lastSelection){
18882                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18883             }else{
18884                 this.select(item, this.multiSelect && e.ctrlKey);
18885                 this.lastSelection = item;
18886             }
18887             
18888             if(!this.tickable){
18889                 e.preventDefault();
18890             }
18891             
18892         }
18893         return true;
18894     },
18895
18896     /**
18897      * Get the number of selected nodes.
18898      * @return {Number}
18899      */
18900     getSelectionCount : function(){
18901         return this.selections.length;
18902     },
18903
18904     /**
18905      * Get the currently selected nodes.
18906      * @return {Array} An array of HTMLElements
18907      */
18908     getSelectedNodes : function(){
18909         return this.selections;
18910     },
18911
18912     /**
18913      * Get the indexes of the selected nodes.
18914      * @return {Array}
18915      */
18916     getSelectedIndexes : function(){
18917         var indexes = [], s = this.selections;
18918         for(var i = 0, len = s.length; i < len; i++){
18919             indexes.push(s[i].nodeIndex);
18920         }
18921         return indexes;
18922     },
18923
18924     /**
18925      * Clear all selections
18926      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18927      */
18928     clearSelections : function(suppressEvent){
18929         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18930             this.cmp.elements = this.selections;
18931             this.cmp.removeClass(this.selectedClass);
18932             this.selections = [];
18933             if(!suppressEvent){
18934                 this.fireEvent("selectionchange", this, this.selections);
18935             }
18936         }
18937     },
18938
18939     /**
18940      * Returns true if the passed node is selected
18941      * @param {HTMLElement/Number} node The node or node index
18942      * @return {Boolean}
18943      */
18944     isSelected : function(node){
18945         var s = this.selections;
18946         if(s.length < 1){
18947             return false;
18948         }
18949         node = this.getNode(node);
18950         return s.indexOf(node) !== -1;
18951     },
18952
18953     /**
18954      * Selects nodes.
18955      * @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
18956      * @param {Boolean} keepExisting (optional) true to keep existing selections
18957      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18958      */
18959     select : function(nodeInfo, keepExisting, suppressEvent){
18960         if(nodeInfo instanceof Array){
18961             if(!keepExisting){
18962                 this.clearSelections(true);
18963             }
18964             for(var i = 0, len = nodeInfo.length; i < len; i++){
18965                 this.select(nodeInfo[i], true, true);
18966             }
18967             return;
18968         } 
18969         var node = this.getNode(nodeInfo);
18970         if(!node || this.isSelected(node)){
18971             return; // already selected.
18972         }
18973         if(!keepExisting){
18974             this.clearSelections(true);
18975         }
18976         
18977         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18978             Roo.fly(node).addClass(this.selectedClass);
18979             this.selections.push(node);
18980             if(!suppressEvent){
18981                 this.fireEvent("selectionchange", this, this.selections);
18982             }
18983         }
18984         
18985         
18986     },
18987       /**
18988      * Unselects nodes.
18989      * @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
18990      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18992      */
18993     unselect : function(nodeInfo, keepExisting, suppressEvent)
18994     {
18995         if(nodeInfo instanceof Array){
18996             Roo.each(this.selections, function(s) {
18997                 this.unselect(s, nodeInfo);
18998             }, this);
18999             return;
19000         }
19001         var node = this.getNode(nodeInfo);
19002         if(!node || !this.isSelected(node)){
19003             //Roo.log("not selected");
19004             return; // not selected.
19005         }
19006         // fireevent???
19007         var ns = [];
19008         Roo.each(this.selections, function(s) {
19009             if (s == node ) {
19010                 Roo.fly(node).removeClass(this.selectedClass);
19011
19012                 return;
19013             }
19014             ns.push(s);
19015         },this);
19016         
19017         this.selections= ns;
19018         this.fireEvent("selectionchange", this, this.selections);
19019     },
19020
19021     /**
19022      * Gets a template node.
19023      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19024      * @return {HTMLElement} The node or null if it wasn't found
19025      */
19026     getNode : function(nodeInfo){
19027         if(typeof nodeInfo == "string"){
19028             return document.getElementById(nodeInfo);
19029         }else if(typeof nodeInfo == "number"){
19030             return this.nodes[nodeInfo];
19031         }
19032         return nodeInfo;
19033     },
19034
19035     /**
19036      * Gets a range template nodes.
19037      * @param {Number} startIndex
19038      * @param {Number} endIndex
19039      * @return {Array} An array of nodes
19040      */
19041     getNodes : function(start, end){
19042         var ns = this.nodes;
19043         start = start || 0;
19044         end = typeof end == "undefined" ? ns.length - 1 : end;
19045         var nodes = [];
19046         if(start <= end){
19047             for(var i = start; i <= end; i++){
19048                 nodes.push(ns[i]);
19049             }
19050         } else{
19051             for(var i = start; i >= end; i--){
19052                 nodes.push(ns[i]);
19053             }
19054         }
19055         return nodes;
19056     },
19057
19058     /**
19059      * Finds the index of the passed node
19060      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19061      * @return {Number} The index of the node or -1
19062      */
19063     indexOf : function(node){
19064         node = this.getNode(node);
19065         if(typeof node.nodeIndex == "number"){
19066             return node.nodeIndex;
19067         }
19068         var ns = this.nodes;
19069         for(var i = 0, len = ns.length; i < len; i++){
19070             if(ns[i] == node){
19071                 return i;
19072             }
19073         }
19074         return -1;
19075     }
19076 });
19077 /*
19078  * - LGPL
19079  *
19080  * based on jquery fullcalendar
19081  * 
19082  */
19083
19084 Roo.bootstrap = Roo.bootstrap || {};
19085 /**
19086  * @class Roo.bootstrap.Calendar
19087  * @extends Roo.bootstrap.Component
19088  * Bootstrap Calendar class
19089  * @cfg {Boolean} loadMask (true|false) default false
19090  * @cfg {Object} header generate the user specific header of the calendar, default false
19091
19092  * @constructor
19093  * Create a new Container
19094  * @param {Object} config The config object
19095  */
19096
19097
19098
19099 Roo.bootstrap.Calendar = function(config){
19100     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19101      this.addEvents({
19102         /**
19103              * @event select
19104              * Fires when a date is selected
19105              * @param {DatePicker} this
19106              * @param {Date} date The selected date
19107              */
19108         'select': true,
19109         /**
19110              * @event monthchange
19111              * Fires when the displayed month changes 
19112              * @param {DatePicker} this
19113              * @param {Date} date The selected month
19114              */
19115         'monthchange': true,
19116         /**
19117              * @event evententer
19118              * Fires when mouse over an event
19119              * @param {Calendar} this
19120              * @param {event} Event
19121              */
19122         'evententer': true,
19123         /**
19124              * @event eventleave
19125              * Fires when the mouse leaves an
19126              * @param {Calendar} this
19127              * @param {event}
19128              */
19129         'eventleave': true,
19130         /**
19131              * @event eventclick
19132              * Fires when the mouse click an
19133              * @param {Calendar} this
19134              * @param {event}
19135              */
19136         'eventclick': true
19137         
19138     });
19139
19140 };
19141
19142 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19143     
19144      /**
19145      * @cfg {Number} startDay
19146      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19147      */
19148     startDay : 0,
19149     
19150     loadMask : false,
19151     
19152     header : false,
19153       
19154     getAutoCreate : function(){
19155         
19156         
19157         var fc_button = function(name, corner, style, content ) {
19158             return Roo.apply({},{
19159                 tag : 'span',
19160                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19161                          (corner.length ?
19162                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19163                             ''
19164                         ),
19165                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19166                 unselectable: 'on'
19167             });
19168         };
19169         
19170         var header = {};
19171         
19172         if(!this.header){
19173             header = {
19174                 tag : 'table',
19175                 cls : 'fc-header',
19176                 style : 'width:100%',
19177                 cn : [
19178                     {
19179                         tag: 'tr',
19180                         cn : [
19181                             {
19182                                 tag : 'td',
19183                                 cls : 'fc-header-left',
19184                                 cn : [
19185                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19186                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19187                                     { tag: 'span', cls: 'fc-header-space' },
19188                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19189
19190
19191                                 ]
19192                             },
19193
19194                             {
19195                                 tag : 'td',
19196                                 cls : 'fc-header-center',
19197                                 cn : [
19198                                     {
19199                                         tag: 'span',
19200                                         cls: 'fc-header-title',
19201                                         cn : {
19202                                             tag: 'H2',
19203                                             html : 'month / year'
19204                                         }
19205                                     }
19206
19207                                 ]
19208                             },
19209                             {
19210                                 tag : 'td',
19211                                 cls : 'fc-header-right',
19212                                 cn : [
19213                               /*      fc_button('month', 'left', '', 'month' ),
19214                                     fc_button('week', '', '', 'week' ),
19215                                     fc_button('day', 'right', '', 'day' )
19216                                 */    
19217
19218                                 ]
19219                             }
19220
19221                         ]
19222                     }
19223                 ]
19224             };
19225         }
19226         
19227         header = this.header;
19228         
19229        
19230         var cal_heads = function() {
19231             var ret = [];
19232             // fixme - handle this.
19233             
19234             for (var i =0; i < Date.dayNames.length; i++) {
19235                 var d = Date.dayNames[i];
19236                 ret.push({
19237                     tag: 'th',
19238                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19239                     html : d.substring(0,3)
19240                 });
19241                 
19242             }
19243             ret[0].cls += ' fc-first';
19244             ret[6].cls += ' fc-last';
19245             return ret;
19246         };
19247         var cal_cell = function(n) {
19248             return  {
19249                 tag: 'td',
19250                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19251                 cn : [
19252                     {
19253                         cn : [
19254                             {
19255                                 cls: 'fc-day-number',
19256                                 html: 'D'
19257                             },
19258                             {
19259                                 cls: 'fc-day-content',
19260                              
19261                                 cn : [
19262                                      {
19263                                         style: 'position: relative;' // height: 17px;
19264                                     }
19265                                 ]
19266                             }
19267                             
19268                             
19269                         ]
19270                     }
19271                 ]
19272                 
19273             }
19274         };
19275         var cal_rows = function() {
19276             
19277             var ret = [];
19278             for (var r = 0; r < 6; r++) {
19279                 var row= {
19280                     tag : 'tr',
19281                     cls : 'fc-week',
19282                     cn : []
19283                 };
19284                 
19285                 for (var i =0; i < Date.dayNames.length; i++) {
19286                     var d = Date.dayNames[i];
19287                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19288
19289                 }
19290                 row.cn[0].cls+=' fc-first';
19291                 row.cn[0].cn[0].style = 'min-height:90px';
19292                 row.cn[6].cls+=' fc-last';
19293                 ret.push(row);
19294                 
19295             }
19296             ret[0].cls += ' fc-first';
19297             ret[4].cls += ' fc-prev-last';
19298             ret[5].cls += ' fc-last';
19299             return ret;
19300             
19301         };
19302         
19303         var cal_table = {
19304             tag: 'table',
19305             cls: 'fc-border-separate',
19306             style : 'width:100%',
19307             cellspacing  : 0,
19308             cn : [
19309                 { 
19310                     tag: 'thead',
19311                     cn : [
19312                         { 
19313                             tag: 'tr',
19314                             cls : 'fc-first fc-last',
19315                             cn : cal_heads()
19316                         }
19317                     ]
19318                 },
19319                 { 
19320                     tag: 'tbody',
19321                     cn : cal_rows()
19322                 }
19323                   
19324             ]
19325         };
19326          
19327          var cfg = {
19328             cls : 'fc fc-ltr',
19329             cn : [
19330                 header,
19331                 {
19332                     cls : 'fc-content',
19333                     style : "position: relative;",
19334                     cn : [
19335                         {
19336                             cls : 'fc-view fc-view-month fc-grid',
19337                             style : 'position: relative',
19338                             unselectable : 'on',
19339                             cn : [
19340                                 {
19341                                     cls : 'fc-event-container',
19342                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19343                                 },
19344                                 cal_table
19345                             ]
19346                         }
19347                     ]
19348     
19349                 }
19350            ] 
19351             
19352         };
19353         
19354          
19355         
19356         return cfg;
19357     },
19358     
19359     
19360     initEvents : function()
19361     {
19362         if(!this.store){
19363             throw "can not find store for calendar";
19364         }
19365         
19366         var mark = {
19367             tag: "div",
19368             cls:"x-dlg-mask",
19369             style: "text-align:center",
19370             cn: [
19371                 {
19372                     tag: "div",
19373                     style: "background-color:white;width:50%;margin:250 auto",
19374                     cn: [
19375                         {
19376                             tag: "img",
19377                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19378                         },
19379                         {
19380                             tag: "span",
19381                             html: "Loading"
19382                         }
19383                         
19384                     ]
19385                 }
19386             ]
19387         };
19388         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19389         
19390         var size = this.el.select('.fc-content', true).first().getSize();
19391         this.maskEl.setSize(size.width, size.height);
19392         this.maskEl.enableDisplayMode("block");
19393         if(!this.loadMask){
19394             this.maskEl.hide();
19395         }
19396         
19397         this.store = Roo.factory(this.store, Roo.data);
19398         this.store.on('load', this.onLoad, this);
19399         this.store.on('beforeload', this.onBeforeLoad, this);
19400         
19401         this.resize();
19402         
19403         this.cells = this.el.select('.fc-day',true);
19404         //Roo.log(this.cells);
19405         this.textNodes = this.el.query('.fc-day-number');
19406         this.cells.addClassOnOver('fc-state-hover');
19407         
19408         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19409         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19410         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19411         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19412         
19413         this.on('monthchange', this.onMonthChange, this);
19414         
19415         this.update(new Date().clearTime());
19416     },
19417     
19418     resize : function() {
19419         var sz  = this.el.getSize();
19420         
19421         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19422         this.el.select('.fc-day-content div',true).setHeight(34);
19423     },
19424     
19425     
19426     // private
19427     showPrevMonth : function(e){
19428         this.update(this.activeDate.add("mo", -1));
19429     },
19430     showToday : function(e){
19431         this.update(new Date().clearTime());
19432     },
19433     // private
19434     showNextMonth : function(e){
19435         this.update(this.activeDate.add("mo", 1));
19436     },
19437
19438     // private
19439     showPrevYear : function(){
19440         this.update(this.activeDate.add("y", -1));
19441     },
19442
19443     // private
19444     showNextYear : function(){
19445         this.update(this.activeDate.add("y", 1));
19446     },
19447
19448     
19449    // private
19450     update : function(date)
19451     {
19452         var vd = this.activeDate;
19453         this.activeDate = date;
19454 //        if(vd && this.el){
19455 //            var t = date.getTime();
19456 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19457 //                Roo.log('using add remove');
19458 //                
19459 //                this.fireEvent('monthchange', this, date);
19460 //                
19461 //                this.cells.removeClass("fc-state-highlight");
19462 //                this.cells.each(function(c){
19463 //                   if(c.dateValue == t){
19464 //                       c.addClass("fc-state-highlight");
19465 //                       setTimeout(function(){
19466 //                            try{c.dom.firstChild.focus();}catch(e){}
19467 //                       }, 50);
19468 //                       return false;
19469 //                   }
19470 //                   return true;
19471 //                });
19472 //                return;
19473 //            }
19474 //        }
19475         
19476         var days = date.getDaysInMonth();
19477         
19478         var firstOfMonth = date.getFirstDateOfMonth();
19479         var startingPos = firstOfMonth.getDay()-this.startDay;
19480         
19481         if(startingPos < this.startDay){
19482             startingPos += 7;
19483         }
19484         
19485         var pm = date.add(Date.MONTH, -1);
19486         var prevStart = pm.getDaysInMonth()-startingPos;
19487 //        
19488         this.cells = this.el.select('.fc-day',true);
19489         this.textNodes = this.el.query('.fc-day-number');
19490         this.cells.addClassOnOver('fc-state-hover');
19491         
19492         var cells = this.cells.elements;
19493         var textEls = this.textNodes;
19494         
19495         Roo.each(cells, function(cell){
19496             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19497         });
19498         
19499         days += startingPos;
19500
19501         // convert everything to numbers so it's fast
19502         var day = 86400000;
19503         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19504         //Roo.log(d);
19505         //Roo.log(pm);
19506         //Roo.log(prevStart);
19507         
19508         var today = new Date().clearTime().getTime();
19509         var sel = date.clearTime().getTime();
19510         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19511         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19512         var ddMatch = this.disabledDatesRE;
19513         var ddText = this.disabledDatesText;
19514         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19515         var ddaysText = this.disabledDaysText;
19516         var format = this.format;
19517         
19518         var setCellClass = function(cal, cell){
19519             cell.row = 0;
19520             cell.events = [];
19521             cell.more = [];
19522             //Roo.log('set Cell Class');
19523             cell.title = "";
19524             var t = d.getTime();
19525             
19526             //Roo.log(d);
19527             
19528             cell.dateValue = t;
19529             if(t == today){
19530                 cell.className += " fc-today";
19531                 cell.className += " fc-state-highlight";
19532                 cell.title = cal.todayText;
19533             }
19534             if(t == sel){
19535                 // disable highlight in other month..
19536                 //cell.className += " fc-state-highlight";
19537                 
19538             }
19539             // disabling
19540             if(t < min) {
19541                 cell.className = " fc-state-disabled";
19542                 cell.title = cal.minText;
19543                 return;
19544             }
19545             if(t > max) {
19546                 cell.className = " fc-state-disabled";
19547                 cell.title = cal.maxText;
19548                 return;
19549             }
19550             if(ddays){
19551                 if(ddays.indexOf(d.getDay()) != -1){
19552                     cell.title = ddaysText;
19553                     cell.className = " fc-state-disabled";
19554                 }
19555             }
19556             if(ddMatch && format){
19557                 var fvalue = d.dateFormat(format);
19558                 if(ddMatch.test(fvalue)){
19559                     cell.title = ddText.replace("%0", fvalue);
19560                     cell.className = " fc-state-disabled";
19561                 }
19562             }
19563             
19564             if (!cell.initialClassName) {
19565                 cell.initialClassName = cell.dom.className;
19566             }
19567             
19568             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19569         };
19570
19571         var i = 0;
19572         
19573         for(; i < startingPos; i++) {
19574             textEls[i].innerHTML = (++prevStart);
19575             d.setDate(d.getDate()+1);
19576             
19577             cells[i].className = "fc-past fc-other-month";
19578             setCellClass(this, cells[i]);
19579         }
19580         
19581         var intDay = 0;
19582         
19583         for(; i < days; i++){
19584             intDay = i - startingPos + 1;
19585             textEls[i].innerHTML = (intDay);
19586             d.setDate(d.getDate()+1);
19587             
19588             cells[i].className = ''; // "x-date-active";
19589             setCellClass(this, cells[i]);
19590         }
19591         var extraDays = 0;
19592         
19593         for(; i < 42; i++) {
19594             textEls[i].innerHTML = (++extraDays);
19595             d.setDate(d.getDate()+1);
19596             
19597             cells[i].className = "fc-future fc-other-month";
19598             setCellClass(this, cells[i]);
19599         }
19600         
19601         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19602         
19603         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19604         
19605         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19606         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19607         
19608         if(totalRows != 6){
19609             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19610             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19611         }
19612         
19613         this.fireEvent('monthchange', this, date);
19614         
19615         
19616         /*
19617         if(!this.internalRender){
19618             var main = this.el.dom.firstChild;
19619             var w = main.offsetWidth;
19620             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19621             Roo.fly(main).setWidth(w);
19622             this.internalRender = true;
19623             // opera does not respect the auto grow header center column
19624             // then, after it gets a width opera refuses to recalculate
19625             // without a second pass
19626             if(Roo.isOpera && !this.secondPass){
19627                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19628                 this.secondPass = true;
19629                 this.update.defer(10, this, [date]);
19630             }
19631         }
19632         */
19633         
19634     },
19635     
19636     findCell : function(dt) {
19637         dt = dt.clearTime().getTime();
19638         var ret = false;
19639         this.cells.each(function(c){
19640             //Roo.log("check " +c.dateValue + '?=' + dt);
19641             if(c.dateValue == dt){
19642                 ret = c;
19643                 return false;
19644             }
19645             return true;
19646         });
19647         
19648         return ret;
19649     },
19650     
19651     findCells : function(ev) {
19652         var s = ev.start.clone().clearTime().getTime();
19653        // Roo.log(s);
19654         var e= ev.end.clone().clearTime().getTime();
19655        // Roo.log(e);
19656         var ret = [];
19657         this.cells.each(function(c){
19658              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19659             
19660             if(c.dateValue > e){
19661                 return ;
19662             }
19663             if(c.dateValue < s){
19664                 return ;
19665             }
19666             ret.push(c);
19667         });
19668         
19669         return ret;    
19670     },
19671     
19672 //    findBestRow: function(cells)
19673 //    {
19674 //        var ret = 0;
19675 //        
19676 //        for (var i =0 ; i < cells.length;i++) {
19677 //            ret  = Math.max(cells[i].rows || 0,ret);
19678 //        }
19679 //        return ret;
19680 //        
19681 //    },
19682     
19683     
19684     addItem : function(ev)
19685     {
19686         // look for vertical location slot in
19687         var cells = this.findCells(ev);
19688         
19689 //        ev.row = this.findBestRow(cells);
19690         
19691         // work out the location.
19692         
19693         var crow = false;
19694         var rows = [];
19695         for(var i =0; i < cells.length; i++) {
19696             
19697             cells[i].row = cells[0].row;
19698             
19699             if(i == 0){
19700                 cells[i].row = cells[i].row + 1;
19701             }
19702             
19703             if (!crow) {
19704                 crow = {
19705                     start : cells[i],
19706                     end :  cells[i]
19707                 };
19708                 continue;
19709             }
19710             if (crow.start.getY() == cells[i].getY()) {
19711                 // on same row.
19712                 crow.end = cells[i];
19713                 continue;
19714             }
19715             // different row.
19716             rows.push(crow);
19717             crow = {
19718                 start: cells[i],
19719                 end : cells[i]
19720             };
19721             
19722         }
19723         
19724         rows.push(crow);
19725         ev.els = [];
19726         ev.rows = rows;
19727         ev.cells = cells;
19728         
19729         cells[0].events.push(ev);
19730         
19731         this.calevents.push(ev);
19732     },
19733     
19734     clearEvents: function() {
19735         
19736         if(!this.calevents){
19737             return;
19738         }
19739         
19740         Roo.each(this.cells.elements, function(c){
19741             c.row = 0;
19742             c.events = [];
19743             c.more = [];
19744         });
19745         
19746         Roo.each(this.calevents, function(e) {
19747             Roo.each(e.els, function(el) {
19748                 el.un('mouseenter' ,this.onEventEnter, this);
19749                 el.un('mouseleave' ,this.onEventLeave, this);
19750                 el.remove();
19751             },this);
19752         },this);
19753         
19754         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19755             e.remove();
19756         });
19757         
19758     },
19759     
19760     renderEvents: function()
19761     {   
19762         var _this = this;
19763         
19764         this.cells.each(function(c) {
19765             
19766             if(c.row < 5){
19767                 return;
19768             }
19769             
19770             var ev = c.events;
19771             
19772             var r = 4;
19773             if(c.row != c.events.length){
19774                 r = 4 - (4 - (c.row - c.events.length));
19775             }
19776             
19777             c.events = ev.slice(0, r);
19778             c.more = ev.slice(r);
19779             
19780             if(c.more.length && c.more.length == 1){
19781                 c.events.push(c.more.pop());
19782             }
19783             
19784             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19785             
19786         });
19787             
19788         this.cells.each(function(c) {
19789             
19790             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19791             
19792             
19793             for (var e = 0; e < c.events.length; e++){
19794                 var ev = c.events[e];
19795                 var rows = ev.rows;
19796                 
19797                 for(var i = 0; i < rows.length; i++) {
19798                 
19799                     // how many rows should it span..
19800
19801                     var  cfg = {
19802                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19803                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19804
19805                         unselectable : "on",
19806                         cn : [
19807                             {
19808                                 cls: 'fc-event-inner',
19809                                 cn : [
19810     //                                {
19811     //                                  tag:'span',
19812     //                                  cls: 'fc-event-time',
19813     //                                  html : cells.length > 1 ? '' : ev.time
19814     //                                },
19815                                     {
19816                                       tag:'span',
19817                                       cls: 'fc-event-title',
19818                                       html : String.format('{0}', ev.title)
19819                                     }
19820
19821
19822                                 ]
19823                             },
19824                             {
19825                                 cls: 'ui-resizable-handle ui-resizable-e',
19826                                 html : '&nbsp;&nbsp;&nbsp'
19827                             }
19828
19829                         ]
19830                     };
19831
19832                     if (i == 0) {
19833                         cfg.cls += ' fc-event-start';
19834                     }
19835                     if ((i+1) == rows.length) {
19836                         cfg.cls += ' fc-event-end';
19837                     }
19838
19839                     var ctr = _this.el.select('.fc-event-container',true).first();
19840                     var cg = ctr.createChild(cfg);
19841
19842                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19843                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19844
19845                     var r = (c.more.length) ? 1 : 0;
19846                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19847                     cg.setWidth(ebox.right - sbox.x -2);
19848
19849                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19850                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19851                     cg.on('click', _this.onEventClick, _this, ev);
19852
19853                     ev.els.push(cg);
19854                     
19855                 }
19856                 
19857             }
19858             
19859             
19860             if(c.more.length){
19861                 var  cfg = {
19862                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19863                     style : 'position: absolute',
19864                     unselectable : "on",
19865                     cn : [
19866                         {
19867                             cls: 'fc-event-inner',
19868                             cn : [
19869                                 {
19870                                   tag:'span',
19871                                   cls: 'fc-event-title',
19872                                   html : 'More'
19873                                 }
19874
19875
19876                             ]
19877                         },
19878                         {
19879                             cls: 'ui-resizable-handle ui-resizable-e',
19880                             html : '&nbsp;&nbsp;&nbsp'
19881                         }
19882
19883                     ]
19884                 };
19885
19886                 var ctr = _this.el.select('.fc-event-container',true).first();
19887                 var cg = ctr.createChild(cfg);
19888
19889                 var sbox = c.select('.fc-day-content',true).first().getBox();
19890                 var ebox = c.select('.fc-day-content',true).first().getBox();
19891                 //Roo.log(cg);
19892                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19893                 cg.setWidth(ebox.right - sbox.x -2);
19894
19895                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19896                 
19897             }
19898             
19899         });
19900         
19901         
19902         
19903     },
19904     
19905     onEventEnter: function (e, el,event,d) {
19906         this.fireEvent('evententer', this, el, event);
19907     },
19908     
19909     onEventLeave: function (e, el,event,d) {
19910         this.fireEvent('eventleave', this, el, event);
19911     },
19912     
19913     onEventClick: function (e, el,event,d) {
19914         this.fireEvent('eventclick', this, el, event);
19915     },
19916     
19917     onMonthChange: function () {
19918         this.store.load();
19919     },
19920     
19921     onMoreEventClick: function(e, el, more)
19922     {
19923         var _this = this;
19924         
19925         this.calpopover.placement = 'right';
19926         this.calpopover.setTitle('More');
19927         
19928         this.calpopover.setContent('');
19929         
19930         var ctr = this.calpopover.el.select('.popover-content', true).first();
19931         
19932         Roo.each(more, function(m){
19933             var cfg = {
19934                 cls : 'fc-event-hori fc-event-draggable',
19935                 html : m.title
19936             };
19937             var cg = ctr.createChild(cfg);
19938             
19939             cg.on('click', _this.onEventClick, _this, m);
19940         });
19941         
19942         this.calpopover.show(el);
19943         
19944         
19945     },
19946     
19947     onLoad: function () 
19948     {   
19949         this.calevents = [];
19950         var cal = this;
19951         
19952         if(this.store.getCount() > 0){
19953             this.store.data.each(function(d){
19954                cal.addItem({
19955                     id : d.data.id,
19956                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19957                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19958                     time : d.data.start_time,
19959                     title : d.data.title,
19960                     description : d.data.description,
19961                     venue : d.data.venue
19962                 });
19963             });
19964         }
19965         
19966         this.renderEvents();
19967         
19968         if(this.calevents.length && this.loadMask){
19969             this.maskEl.hide();
19970         }
19971     },
19972     
19973     onBeforeLoad: function()
19974     {
19975         this.clearEvents();
19976         if(this.loadMask){
19977             this.maskEl.show();
19978         }
19979     }
19980 });
19981
19982  
19983  /*
19984  * - LGPL
19985  *
19986  * element
19987  * 
19988  */
19989
19990 /**
19991  * @class Roo.bootstrap.Popover
19992  * @extends Roo.bootstrap.Component
19993  * Bootstrap Popover class
19994  * @cfg {String} html contents of the popover   (or false to use children..)
19995  * @cfg {String} title of popover (or false to hide)
19996  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19997  * @cfg {String} trigger click || hover (or false to trigger manually)
19998  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19999  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20000  *      - if false and it has a 'parent' then it will be automatically added to that element
20001  *      - if string - Roo.get  will be called 
20002  * @cfg {Number} delay - delay before showing
20003  
20004  * @constructor
20005  * Create a new Popover
20006  * @param {Object} config The config object
20007  */
20008
20009 Roo.bootstrap.Popover = function(config){
20010     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20011     
20012     this.addEvents({
20013         // raw events
20014          /**
20015          * @event show
20016          * After the popover show
20017          * 
20018          * @param {Roo.bootstrap.Popover} this
20019          */
20020         "show" : true,
20021         /**
20022          * @event hide
20023          * After the popover hide
20024          * 
20025          * @param {Roo.bootstrap.Popover} this
20026          */
20027         "hide" : true
20028     });
20029 };
20030
20031 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20032     
20033     title: false,
20034     html: false,
20035     
20036     placement : 'right',
20037     trigger : 'hover', // hover
20038     modal : false,
20039     delay : 0,
20040     
20041     over: false,
20042     
20043     can_build_overlaid : false,
20044     
20045     maskEl : false, // the mask element
20046     headerEl : false,
20047     contentEl : false,
20048     alignEl : false, // when show is called with an element - this get's stored.
20049     
20050     getChildContainer : function()
20051     {
20052         return this.contentEl;
20053         
20054     },
20055     getPopoverHeader : function()
20056     {
20057         this.title = true; // flag not to hide it..
20058         this.headerEl.addClass('p-0');
20059         return this.headerEl
20060     },
20061     
20062     
20063     getAutoCreate : function(){
20064          
20065         var cfg = {
20066            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20067            style: 'display:block',
20068            cn : [
20069                 {
20070                     cls : 'arrow'
20071                 },
20072                 {
20073                     cls : 'popover-inner ',
20074                     cn : [
20075                         {
20076                             tag: 'h3',
20077                             cls: 'popover-title popover-header',
20078                             html : this.title === false ? '' : this.title
20079                         },
20080                         {
20081                             cls : 'popover-content popover-body '  + (this.cls || ''),
20082                             html : this.html || ''
20083                         }
20084                     ]
20085                     
20086                 }
20087            ]
20088         };
20089         
20090         return cfg;
20091     },
20092     /**
20093      * @param {string} the title
20094      */
20095     setTitle: function(str)
20096     {
20097         this.title = str;
20098         if (this.el) {
20099             this.headerEl.dom.innerHTML = str;
20100         }
20101         
20102     },
20103     /**
20104      * @param {string} the body content
20105      */
20106     setContent: function(str)
20107     {
20108         this.html = str;
20109         if (this.contentEl) {
20110             this.contentEl.dom.innerHTML = str;
20111         }
20112         
20113     },
20114     // as it get's added to the bottom of the page.
20115     onRender : function(ct, position)
20116     {
20117         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20118         
20119         
20120         
20121         if(!this.el){
20122             var cfg = Roo.apply({},  this.getAutoCreate());
20123             cfg.id = Roo.id();
20124             
20125             if (this.cls) {
20126                 cfg.cls += ' ' + this.cls;
20127             }
20128             if (this.style) {
20129                 cfg.style = this.style;
20130             }
20131             //Roo.log("adding to ");
20132             this.el = Roo.get(document.body).createChild(cfg, position);
20133 //            Roo.log(this.el);
20134         }
20135         
20136         this.contentEl = this.el.select('.popover-content',true).first();
20137         this.headerEl =  this.el.select('.popover-title',true).first();
20138         
20139         var nitems = [];
20140         if(typeof(this.items) != 'undefined'){
20141             var items = this.items;
20142             delete this.items;
20143
20144             for(var i =0;i < items.length;i++) {
20145                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20146             }
20147         }
20148
20149         this.items = nitems;
20150         
20151         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20152         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20153         
20154         
20155         
20156         this.initEvents();
20157     },
20158     
20159     resizeMask : function()
20160     {
20161         this.maskEl.setSize(
20162             Roo.lib.Dom.getViewWidth(true),
20163             Roo.lib.Dom.getViewHeight(true)
20164         );
20165     },
20166     
20167     initEvents : function()
20168     {
20169         
20170         if (!this.modal) { 
20171             Roo.bootstrap.Popover.register(this);
20172         }
20173          
20174         this.arrowEl = this.el.select('.arrow',true).first();
20175         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20176         this.el.enableDisplayMode('block');
20177         this.el.hide();
20178  
20179         
20180         if (this.over === false && !this.parent()) {
20181             return; 
20182         }
20183         if (this.triggers === false) {
20184             return;
20185         }
20186          
20187         // support parent
20188         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20189         var triggers = this.trigger ? this.trigger.split(' ') : [];
20190         Roo.each(triggers, function(trigger) {
20191         
20192             if (trigger == 'click') {
20193                 on_el.on('click', this.toggle, this);
20194             } else if (trigger != 'manual') {
20195                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20196                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20197       
20198                 on_el.on(eventIn  ,this.enter, this);
20199                 on_el.on(eventOut, this.leave, this);
20200             }
20201         }, this);
20202     },
20203     
20204     
20205     // private
20206     timeout : null,
20207     hoverState : null,
20208     
20209     toggle : function () {
20210         this.hoverState == 'in' ? this.leave() : this.enter();
20211     },
20212     
20213     enter : function () {
20214         
20215         clearTimeout(this.timeout);
20216     
20217         this.hoverState = 'in';
20218     
20219         if (!this.delay || !this.delay.show) {
20220             this.show();
20221             return;
20222         }
20223         var _t = this;
20224         this.timeout = setTimeout(function () {
20225             if (_t.hoverState == 'in') {
20226                 _t.show();
20227             }
20228         }, this.delay.show)
20229     },
20230     
20231     leave : function() {
20232         clearTimeout(this.timeout);
20233     
20234         this.hoverState = 'out';
20235     
20236         if (!this.delay || !this.delay.hide) {
20237             this.hide();
20238             return;
20239         }
20240         var _t = this;
20241         this.timeout = setTimeout(function () {
20242             if (_t.hoverState == 'out') {
20243                 _t.hide();
20244             }
20245         }, this.delay.hide)
20246     },
20247     /**
20248      * Show the popover
20249      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20250      * @param {string} (left|right|top|bottom) position
20251      */
20252     show : function (on_el, placement)
20253     {
20254         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20255         on_el = on_el || false; // default to false
20256          
20257         if (!on_el) {
20258             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20259                 on_el = this.parent().el;
20260             } else if (this.over) {
20261                 on_el = Roo.get(this.over);
20262             }
20263             
20264         }
20265         
20266         this.alignEl = Roo.get( on_el );
20267
20268         if (!this.el) {
20269             this.render(document.body);
20270         }
20271         
20272         
20273          
20274         
20275         if (this.title === false) {
20276             this.headerEl.hide();
20277         }
20278         
20279        
20280         this.el.show();
20281         this.el.dom.style.display = 'block';
20282          
20283  
20284         if (this.alignEl) {
20285             this.updatePosition(this.placement, true);
20286              
20287         } else {
20288             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20289             var es = this.el.getSize();
20290             var x = Roo.lib.Dom.getViewWidth()/2;
20291             var y = Roo.lib.Dom.getViewHeight()/2;
20292             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20293             
20294         }
20295
20296         
20297         //var arrow = this.el.select('.arrow',true).first();
20298         //arrow.set(align[2], 
20299         
20300         this.el.addClass('in');
20301         
20302          
20303         
20304         this.hoverState = 'in';
20305         
20306         if (this.modal) {
20307             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20308             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20309             this.maskEl.dom.style.display = 'block';
20310             this.maskEl.addClass('show');
20311         }
20312         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20313  
20314         this.fireEvent('show', this);
20315         
20316     },
20317     /**
20318      * fire this manually after loading a grid in the table for example
20319      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20320      * @param {Boolean} try and move it if we cant get right position.
20321      */
20322     updatePosition : function(placement, try_move)
20323     {
20324         // allow for calling with no parameters
20325         placement = placement   ? placement :  this.placement;
20326         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20327         
20328         this.el.removeClass([
20329             'fade','top','bottom', 'left', 'right','in',
20330             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20331         ]);
20332         this.el.addClass(placement + ' bs-popover-' + placement);
20333         
20334         if (!this.alignEl ) {
20335             return false;
20336         }
20337         
20338         switch (placement) {
20339             case 'right':
20340                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20341                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20342                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20343                     //normal display... or moved up/down.
20344                     this.el.setXY(offset);
20345                     var xy = this.alignEl.getAnchorXY('tr', false);
20346                     xy[0]+=2;xy[1]+=5;
20347                     this.arrowEl.setXY(xy);
20348                     return true;
20349                 }
20350                 // continue through...
20351                 return this.updatePosition('left', false);
20352                 
20353             
20354             case 'left':
20355                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20356                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20357                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20358                     //normal display... or moved up/down.
20359                     this.el.setXY(offset);
20360                     var xy = this.alignEl.getAnchorXY('tl', false);
20361                     xy[0]-=10;xy[1]+=5; // << fix me
20362                     this.arrowEl.setXY(xy);
20363                     return true;
20364                 }
20365                 // call self...
20366                 return this.updatePosition('right', false);
20367             
20368             case 'top':
20369                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20370                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20371                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20372                     //normal display... or moved up/down.
20373                     this.el.setXY(offset);
20374                     var xy = this.alignEl.getAnchorXY('t', false);
20375                     xy[1]-=10; // << fix me
20376                     this.arrowEl.setXY(xy);
20377                     return true;
20378                 }
20379                 // fall through
20380                return this.updatePosition('bottom', false);
20381             
20382             case 'bottom':
20383                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20384                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20385                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20386                     //normal display... or moved up/down.
20387                     this.el.setXY(offset);
20388                     var xy = this.alignEl.getAnchorXY('b', false);
20389                      xy[1]+=2; // << fix me
20390                     this.arrowEl.setXY(xy);
20391                     return true;
20392                 }
20393                 // fall through
20394                 return this.updatePosition('top', false);
20395                 
20396             
20397         }
20398         
20399         
20400         return false;
20401     },
20402     
20403     hide : function()
20404     {
20405         this.el.setXY([0,0]);
20406         this.el.removeClass('in');
20407         this.el.hide();
20408         this.hoverState = null;
20409         this.maskEl.hide(); // always..
20410         this.fireEvent('hide', this);
20411     }
20412     
20413 });
20414
20415
20416 Roo.apply(Roo.bootstrap.Popover, {
20417
20418     alignment : {
20419         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20420         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20421         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20422         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20423     },
20424     
20425     zIndex : 20001,
20426
20427     clickHander : false,
20428     
20429     
20430
20431     onMouseDown : function(e)
20432     {
20433         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20434             /// what is nothing is showing..
20435             this.hideAll();
20436         }
20437          
20438     },
20439     
20440     
20441     popups : [],
20442     
20443     register : function(popup)
20444     {
20445         if (!Roo.bootstrap.Popover.clickHandler) {
20446             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20447         }
20448         // hide other popups.
20449         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20450         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20451         this.hideAll(); //<< why?
20452         //this.popups.push(popup);
20453     },
20454     hideAll : function()
20455     {
20456         this.popups.forEach(function(p) {
20457             p.hide();
20458         });
20459     },
20460     onShow : function() {
20461         Roo.bootstrap.Popover.popups.push(this);
20462     },
20463     onHide : function() {
20464         Roo.bootstrap.Popover.popups.remove(this);
20465     } 
20466
20467 });/*
20468  * - LGPL
20469  *
20470  * Card header - holder for the card header elements.
20471  * 
20472  */
20473
20474 /**
20475  * @class Roo.bootstrap.PopoverNav
20476  * @extends Roo.bootstrap.NavGroup
20477  * Bootstrap Popover header navigation class
20478  * @constructor
20479  * Create a new Popover Header Navigation 
20480  * @param {Object} config The config object
20481  */
20482
20483 Roo.bootstrap.PopoverNav = function(config){
20484     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20485 };
20486
20487 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20488     
20489     
20490     container_method : 'getPopoverHeader' 
20491     
20492      
20493     
20494     
20495    
20496 });
20497
20498  
20499
20500  /*
20501  * - LGPL
20502  *
20503  * Progress
20504  * 
20505  */
20506
20507 /**
20508  * @class Roo.bootstrap.Progress
20509  * @extends Roo.bootstrap.Component
20510  * Bootstrap Progress class
20511  * @cfg {Boolean} striped striped of the progress bar
20512  * @cfg {Boolean} active animated of the progress bar
20513  * 
20514  * 
20515  * @constructor
20516  * Create a new Progress
20517  * @param {Object} config The config object
20518  */
20519
20520 Roo.bootstrap.Progress = function(config){
20521     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20522 };
20523
20524 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20525     
20526     striped : false,
20527     active: false,
20528     
20529     getAutoCreate : function(){
20530         var cfg = {
20531             tag: 'div',
20532             cls: 'progress'
20533         };
20534         
20535         
20536         if(this.striped){
20537             cfg.cls += ' progress-striped';
20538         }
20539       
20540         if(this.active){
20541             cfg.cls += ' active';
20542         }
20543         
20544         
20545         return cfg;
20546     }
20547    
20548 });
20549
20550  
20551
20552  /*
20553  * - LGPL
20554  *
20555  * ProgressBar
20556  * 
20557  */
20558
20559 /**
20560  * @class Roo.bootstrap.ProgressBar
20561  * @extends Roo.bootstrap.Component
20562  * Bootstrap ProgressBar class
20563  * @cfg {Number} aria_valuenow aria-value now
20564  * @cfg {Number} aria_valuemin aria-value min
20565  * @cfg {Number} aria_valuemax aria-value max
20566  * @cfg {String} label label for the progress bar
20567  * @cfg {String} panel (success | info | warning | danger )
20568  * @cfg {String} role role of the progress bar
20569  * @cfg {String} sr_only text
20570  * 
20571  * 
20572  * @constructor
20573  * Create a new ProgressBar
20574  * @param {Object} config The config object
20575  */
20576
20577 Roo.bootstrap.ProgressBar = function(config){
20578     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20579 };
20580
20581 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20582     
20583     aria_valuenow : 0,
20584     aria_valuemin : 0,
20585     aria_valuemax : 100,
20586     label : false,
20587     panel : false,
20588     role : false,
20589     sr_only: false,
20590     
20591     getAutoCreate : function()
20592     {
20593         
20594         var cfg = {
20595             tag: 'div',
20596             cls: 'progress-bar',
20597             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20598         };
20599         
20600         if(this.sr_only){
20601             cfg.cn = {
20602                 tag: 'span',
20603                 cls: 'sr-only',
20604                 html: this.sr_only
20605             }
20606         }
20607         
20608         if(this.role){
20609             cfg.role = this.role;
20610         }
20611         
20612         if(this.aria_valuenow){
20613             cfg['aria-valuenow'] = this.aria_valuenow;
20614         }
20615         
20616         if(this.aria_valuemin){
20617             cfg['aria-valuemin'] = this.aria_valuemin;
20618         }
20619         
20620         if(this.aria_valuemax){
20621             cfg['aria-valuemax'] = this.aria_valuemax;
20622         }
20623         
20624         if(this.label && !this.sr_only){
20625             cfg.html = this.label;
20626         }
20627         
20628         if(this.panel){
20629             cfg.cls += ' progress-bar-' + this.panel;
20630         }
20631         
20632         return cfg;
20633     },
20634     
20635     update : function(aria_valuenow)
20636     {
20637         this.aria_valuenow = aria_valuenow;
20638         
20639         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20640     }
20641    
20642 });
20643
20644  
20645
20646  /*
20647  * - LGPL
20648  *
20649  * column
20650  * 
20651  */
20652
20653 /**
20654  * @class Roo.bootstrap.TabGroup
20655  * @extends Roo.bootstrap.Column
20656  * Bootstrap Column class
20657  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20658  * @cfg {Boolean} carousel true to make the group behave like a carousel
20659  * @cfg {Boolean} bullets show bullets for the panels
20660  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20661  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20662  * @cfg {Boolean} showarrow (true|false) show arrow default true
20663  * 
20664  * @constructor
20665  * Create a new TabGroup
20666  * @param {Object} config The config object
20667  */
20668
20669 Roo.bootstrap.TabGroup = function(config){
20670     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20671     if (!this.navId) {
20672         this.navId = Roo.id();
20673     }
20674     this.tabs = [];
20675     Roo.bootstrap.TabGroup.register(this);
20676     
20677 };
20678
20679 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20680     
20681     carousel : false,
20682     transition : false,
20683     bullets : 0,
20684     timer : 0,
20685     autoslide : false,
20686     slideFn : false,
20687     slideOnTouch : false,
20688     showarrow : true,
20689     
20690     getAutoCreate : function()
20691     {
20692         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20693         
20694         cfg.cls += ' tab-content';
20695         
20696         if (this.carousel) {
20697             cfg.cls += ' carousel slide';
20698             
20699             cfg.cn = [{
20700                cls : 'carousel-inner',
20701                cn : []
20702             }];
20703         
20704             if(this.bullets  && !Roo.isTouch){
20705                 
20706                 var bullets = {
20707                     cls : 'carousel-bullets',
20708                     cn : []
20709                 };
20710                
20711                 if(this.bullets_cls){
20712                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20713                 }
20714                 
20715                 bullets.cn.push({
20716                     cls : 'clear'
20717                 });
20718                 
20719                 cfg.cn[0].cn.push(bullets);
20720             }
20721             
20722             if(this.showarrow){
20723                 cfg.cn[0].cn.push({
20724                     tag : 'div',
20725                     class : 'carousel-arrow',
20726                     cn : [
20727                         {
20728                             tag : 'div',
20729                             class : 'carousel-prev',
20730                             cn : [
20731                                 {
20732                                     tag : 'i',
20733                                     class : 'fa fa-chevron-left'
20734                                 }
20735                             ]
20736                         },
20737                         {
20738                             tag : 'div',
20739                             class : 'carousel-next',
20740                             cn : [
20741                                 {
20742                                     tag : 'i',
20743                                     class : 'fa fa-chevron-right'
20744                                 }
20745                             ]
20746                         }
20747                     ]
20748                 });
20749             }
20750             
20751         }
20752         
20753         return cfg;
20754     },
20755     
20756     initEvents:  function()
20757     {
20758 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20759 //            this.el.on("touchstart", this.onTouchStart, this);
20760 //        }
20761         
20762         if(this.autoslide){
20763             var _this = this;
20764             
20765             this.slideFn = window.setInterval(function() {
20766                 _this.showPanelNext();
20767             }, this.timer);
20768         }
20769         
20770         if(this.showarrow){
20771             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20772             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20773         }
20774         
20775         
20776     },
20777     
20778 //    onTouchStart : function(e, el, o)
20779 //    {
20780 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20781 //            return;
20782 //        }
20783 //        
20784 //        this.showPanelNext();
20785 //    },
20786     
20787     
20788     getChildContainer : function()
20789     {
20790         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20791     },
20792     
20793     /**
20794     * register a Navigation item
20795     * @param {Roo.bootstrap.NavItem} the navitem to add
20796     */
20797     register : function(item)
20798     {
20799         this.tabs.push( item);
20800         item.navId = this.navId; // not really needed..
20801         this.addBullet();
20802     
20803     },
20804     
20805     getActivePanel : function()
20806     {
20807         var r = false;
20808         Roo.each(this.tabs, function(t) {
20809             if (t.active) {
20810                 r = t;
20811                 return false;
20812             }
20813             return null;
20814         });
20815         return r;
20816         
20817     },
20818     getPanelByName : function(n)
20819     {
20820         var r = false;
20821         Roo.each(this.tabs, function(t) {
20822             if (t.tabId == n) {
20823                 r = t;
20824                 return false;
20825             }
20826             return null;
20827         });
20828         return r;
20829     },
20830     indexOfPanel : function(p)
20831     {
20832         var r = false;
20833         Roo.each(this.tabs, function(t,i) {
20834             if (t.tabId == p.tabId) {
20835                 r = i;
20836                 return false;
20837             }
20838             return null;
20839         });
20840         return r;
20841     },
20842     /**
20843      * show a specific panel
20844      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20845      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20846      */
20847     showPanel : function (pan)
20848     {
20849         if(this.transition || typeof(pan) == 'undefined'){
20850             Roo.log("waiting for the transitionend");
20851             return false;
20852         }
20853         
20854         if (typeof(pan) == 'number') {
20855             pan = this.tabs[pan];
20856         }
20857         
20858         if (typeof(pan) == 'string') {
20859             pan = this.getPanelByName(pan);
20860         }
20861         
20862         var cur = this.getActivePanel();
20863         
20864         if(!pan || !cur){
20865             Roo.log('pan or acitve pan is undefined');
20866             return false;
20867         }
20868         
20869         if (pan.tabId == this.getActivePanel().tabId) {
20870             return true;
20871         }
20872         
20873         if (false === cur.fireEvent('beforedeactivate')) {
20874             return false;
20875         }
20876         
20877         if(this.bullets > 0 && !Roo.isTouch){
20878             this.setActiveBullet(this.indexOfPanel(pan));
20879         }
20880         
20881         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20882             
20883             //class="carousel-item carousel-item-next carousel-item-left"
20884             
20885             this.transition = true;
20886             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20887             var lr = dir == 'next' ? 'left' : 'right';
20888             pan.el.addClass(dir); // or prev
20889             pan.el.addClass('carousel-item-' + dir); // or prev
20890             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20891             cur.el.addClass(lr); // or right
20892             pan.el.addClass(lr);
20893             cur.el.addClass('carousel-item-' +lr); // or right
20894             pan.el.addClass('carousel-item-' +lr);
20895             
20896             
20897             var _this = this;
20898             cur.el.on('transitionend', function() {
20899                 Roo.log("trans end?");
20900                 
20901                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20902                 pan.setActive(true);
20903                 
20904                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20905                 cur.setActive(false);
20906                 
20907                 _this.transition = false;
20908                 
20909             }, this, { single:  true } );
20910             
20911             return true;
20912         }
20913         
20914         cur.setActive(false);
20915         pan.setActive(true);
20916         
20917         return true;
20918         
20919     },
20920     showPanelNext : function()
20921     {
20922         var i = this.indexOfPanel(this.getActivePanel());
20923         
20924         if (i >= this.tabs.length - 1 && !this.autoslide) {
20925             return;
20926         }
20927         
20928         if (i >= this.tabs.length - 1 && this.autoslide) {
20929             i = -1;
20930         }
20931         
20932         this.showPanel(this.tabs[i+1]);
20933     },
20934     
20935     showPanelPrev : function()
20936     {
20937         var i = this.indexOfPanel(this.getActivePanel());
20938         
20939         if (i  < 1 && !this.autoslide) {
20940             return;
20941         }
20942         
20943         if (i < 1 && this.autoslide) {
20944             i = this.tabs.length;
20945         }
20946         
20947         this.showPanel(this.tabs[i-1]);
20948     },
20949     
20950     
20951     addBullet: function()
20952     {
20953         if(!this.bullets || Roo.isTouch){
20954             return;
20955         }
20956         var ctr = this.el.select('.carousel-bullets',true).first();
20957         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20958         var bullet = ctr.createChild({
20959             cls : 'bullet bullet-' + i
20960         },ctr.dom.lastChild);
20961         
20962         
20963         var _this = this;
20964         
20965         bullet.on('click', (function(e, el, o, ii, t){
20966
20967             e.preventDefault();
20968
20969             this.showPanel(ii);
20970
20971             if(this.autoslide && this.slideFn){
20972                 clearInterval(this.slideFn);
20973                 this.slideFn = window.setInterval(function() {
20974                     _this.showPanelNext();
20975                 }, this.timer);
20976             }
20977
20978         }).createDelegate(this, [i, bullet], true));
20979                 
20980         
20981     },
20982      
20983     setActiveBullet : function(i)
20984     {
20985         if(Roo.isTouch){
20986             return;
20987         }
20988         
20989         Roo.each(this.el.select('.bullet', true).elements, function(el){
20990             el.removeClass('selected');
20991         });
20992
20993         var bullet = this.el.select('.bullet-' + i, true).first();
20994         
20995         if(!bullet){
20996             return;
20997         }
20998         
20999         bullet.addClass('selected');
21000     }
21001     
21002     
21003   
21004 });
21005
21006  
21007
21008  
21009  
21010 Roo.apply(Roo.bootstrap.TabGroup, {
21011     
21012     groups: {},
21013      /**
21014     * register a Navigation Group
21015     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21016     */
21017     register : function(navgrp)
21018     {
21019         this.groups[navgrp.navId] = navgrp;
21020         
21021     },
21022     /**
21023     * fetch a Navigation Group based on the navigation ID
21024     * if one does not exist , it will get created.
21025     * @param {string} the navgroup to add
21026     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21027     */
21028     get: function(navId) {
21029         if (typeof(this.groups[navId]) == 'undefined') {
21030             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21031         }
21032         return this.groups[navId] ;
21033     }
21034     
21035     
21036     
21037 });
21038
21039  /*
21040  * - LGPL
21041  *
21042  * TabPanel
21043  * 
21044  */
21045
21046 /**
21047  * @class Roo.bootstrap.TabPanel
21048  * @extends Roo.bootstrap.Component
21049  * Bootstrap TabPanel class
21050  * @cfg {Boolean} active panel active
21051  * @cfg {String} html panel content
21052  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21053  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21054  * @cfg {String} href click to link..
21055  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21056  * 
21057  * 
21058  * @constructor
21059  * Create a new TabPanel
21060  * @param {Object} config The config object
21061  */
21062
21063 Roo.bootstrap.TabPanel = function(config){
21064     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21065     this.addEvents({
21066         /**
21067              * @event changed
21068              * Fires when the active status changes
21069              * @param {Roo.bootstrap.TabPanel} this
21070              * @param {Boolean} state the new state
21071             
21072          */
21073         'changed': true,
21074         /**
21075              * @event beforedeactivate
21076              * Fires before a tab is de-activated - can be used to do validation on a form.
21077              * @param {Roo.bootstrap.TabPanel} this
21078              * @return {Boolean} false if there is an error
21079             
21080          */
21081         'beforedeactivate': true
21082      });
21083     
21084     this.tabId = this.tabId || Roo.id();
21085   
21086 };
21087
21088 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21089     
21090     active: false,
21091     html: false,
21092     tabId: false,
21093     navId : false,
21094     href : '',
21095     touchSlide : false,
21096     getAutoCreate : function(){
21097         
21098         
21099         var cfg = {
21100             tag: 'div',
21101             // item is needed for carousel - not sure if it has any effect otherwise
21102             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21103             html: this.html || ''
21104         };
21105         
21106         if(this.active){
21107             cfg.cls += ' active';
21108         }
21109         
21110         if(this.tabId){
21111             cfg.tabId = this.tabId;
21112         }
21113         
21114         
21115         
21116         return cfg;
21117     },
21118     
21119     initEvents:  function()
21120     {
21121         var p = this.parent();
21122         
21123         this.navId = this.navId || p.navId;
21124         
21125         if (typeof(this.navId) != 'undefined') {
21126             // not really needed.. but just in case.. parent should be a NavGroup.
21127             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21128             
21129             tg.register(this);
21130             
21131             var i = tg.tabs.length - 1;
21132             
21133             if(this.active && tg.bullets > 0 && i < tg.bullets){
21134                 tg.setActiveBullet(i);
21135             }
21136         }
21137         
21138         this.el.on('click', this.onClick, this);
21139         
21140         if(Roo.isTouch && this.touchSlide){
21141             this.el.on("touchstart", this.onTouchStart, this);
21142             this.el.on("touchmove", this.onTouchMove, this);
21143             this.el.on("touchend", this.onTouchEnd, this);
21144         }
21145         
21146     },
21147     
21148     onRender : function(ct, position)
21149     {
21150         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21151     },
21152     
21153     setActive : function(state)
21154     {
21155         Roo.log("panel - set active " + this.tabId + "=" + state);
21156         
21157         this.active = state;
21158         if (!state) {
21159             this.el.removeClass('active');
21160             
21161         } else  if (!this.el.hasClass('active')) {
21162             this.el.addClass('active');
21163         }
21164         
21165         this.fireEvent('changed', this, state);
21166     },
21167     
21168     onClick : function(e)
21169     {
21170         e.preventDefault();
21171         
21172         if(!this.href.length){
21173             return;
21174         }
21175         
21176         window.location.href = this.href;
21177     },
21178     
21179     startX : 0,
21180     startY : 0,
21181     endX : 0,
21182     endY : 0,
21183     swiping : false,
21184     
21185     onTouchStart : function(e)
21186     {
21187         this.swiping = false;
21188         
21189         this.startX = e.browserEvent.touches[0].clientX;
21190         this.startY = e.browserEvent.touches[0].clientY;
21191     },
21192     
21193     onTouchMove : function(e)
21194     {
21195         this.swiping = true;
21196         
21197         this.endX = e.browserEvent.touches[0].clientX;
21198         this.endY = e.browserEvent.touches[0].clientY;
21199     },
21200     
21201     onTouchEnd : function(e)
21202     {
21203         if(!this.swiping){
21204             this.onClick(e);
21205             return;
21206         }
21207         
21208         var tabGroup = this.parent();
21209         
21210         if(this.endX > this.startX){ // swiping right
21211             tabGroup.showPanelPrev();
21212             return;
21213         }
21214         
21215         if(this.startX > this.endX){ // swiping left
21216             tabGroup.showPanelNext();
21217             return;
21218         }
21219     }
21220     
21221     
21222 });
21223  
21224
21225  
21226
21227  /*
21228  * - LGPL
21229  *
21230  * DateField
21231  * 
21232  */
21233
21234 /**
21235  * @class Roo.bootstrap.DateField
21236  * @extends Roo.bootstrap.Input
21237  * Bootstrap DateField class
21238  * @cfg {Number} weekStart default 0
21239  * @cfg {String} viewMode default empty, (months|years)
21240  * @cfg {String} minViewMode default empty, (months|years)
21241  * @cfg {Number} startDate default -Infinity
21242  * @cfg {Number} endDate default Infinity
21243  * @cfg {Boolean} todayHighlight default false
21244  * @cfg {Boolean} todayBtn default false
21245  * @cfg {Boolean} calendarWeeks default false
21246  * @cfg {Object} daysOfWeekDisabled default empty
21247  * @cfg {Boolean} singleMode default false (true | false)
21248  * 
21249  * @cfg {Boolean} keyboardNavigation default true
21250  * @cfg {String} language default en
21251  * 
21252  * @constructor
21253  * Create a new DateField
21254  * @param {Object} config The config object
21255  */
21256
21257 Roo.bootstrap.DateField = function(config){
21258     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21259      this.addEvents({
21260             /**
21261              * @event show
21262              * Fires when this field show.
21263              * @param {Roo.bootstrap.DateField} this
21264              * @param {Mixed} date The date value
21265              */
21266             show : true,
21267             /**
21268              * @event show
21269              * Fires when this field hide.
21270              * @param {Roo.bootstrap.DateField} this
21271              * @param {Mixed} date The date value
21272              */
21273             hide : true,
21274             /**
21275              * @event select
21276              * Fires when select a date.
21277              * @param {Roo.bootstrap.DateField} this
21278              * @param {Mixed} date The date value
21279              */
21280             select : true,
21281             /**
21282              * @event beforeselect
21283              * Fires when before select a date.
21284              * @param {Roo.bootstrap.DateField} this
21285              * @param {Mixed} date The date value
21286              */
21287             beforeselect : true
21288         });
21289 };
21290
21291 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21292     
21293     /**
21294      * @cfg {String} format
21295      * The default date format string which can be overriden for localization support.  The format must be
21296      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21297      */
21298     format : "m/d/y",
21299     /**
21300      * @cfg {String} altFormats
21301      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21302      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21303      */
21304     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21305     
21306     weekStart : 0,
21307     
21308     viewMode : '',
21309     
21310     minViewMode : '',
21311     
21312     todayHighlight : false,
21313     
21314     todayBtn: false,
21315     
21316     language: 'en',
21317     
21318     keyboardNavigation: true,
21319     
21320     calendarWeeks: false,
21321     
21322     startDate: -Infinity,
21323     
21324     endDate: Infinity,
21325     
21326     daysOfWeekDisabled: [],
21327     
21328     _events: [],
21329     
21330     singleMode : false,
21331     
21332     UTCDate: function()
21333     {
21334         return new Date(Date.UTC.apply(Date, arguments));
21335     },
21336     
21337     UTCToday: function()
21338     {
21339         var today = new Date();
21340         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21341     },
21342     
21343     getDate: function() {
21344             var d = this.getUTCDate();
21345             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21346     },
21347     
21348     getUTCDate: function() {
21349             return this.date;
21350     },
21351     
21352     setDate: function(d) {
21353             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21354     },
21355     
21356     setUTCDate: function(d) {
21357             this.date = d;
21358             this.setValue(this.formatDate(this.date));
21359     },
21360         
21361     onRender: function(ct, position)
21362     {
21363         
21364         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21365         
21366         this.language = this.language || 'en';
21367         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21368         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21369         
21370         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21371         this.format = this.format || 'm/d/y';
21372         this.isInline = false;
21373         this.isInput = true;
21374         this.component = this.el.select('.add-on', true).first() || false;
21375         this.component = (this.component && this.component.length === 0) ? false : this.component;
21376         this.hasInput = this.component && this.inputEl().length;
21377         
21378         if (typeof(this.minViewMode === 'string')) {
21379             switch (this.minViewMode) {
21380                 case 'months':
21381                     this.minViewMode = 1;
21382                     break;
21383                 case 'years':
21384                     this.minViewMode = 2;
21385                     break;
21386                 default:
21387                     this.minViewMode = 0;
21388                     break;
21389             }
21390         }
21391         
21392         if (typeof(this.viewMode === 'string')) {
21393             switch (this.viewMode) {
21394                 case 'months':
21395                     this.viewMode = 1;
21396                     break;
21397                 case 'years':
21398                     this.viewMode = 2;
21399                     break;
21400                 default:
21401                     this.viewMode = 0;
21402                     break;
21403             }
21404         }
21405                 
21406         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21407         
21408 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21409         
21410         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21411         
21412         this.picker().on('mousedown', this.onMousedown, this);
21413         this.picker().on('click', this.onClick, this);
21414         
21415         this.picker().addClass('datepicker-dropdown');
21416         
21417         this.startViewMode = this.viewMode;
21418         
21419         if(this.singleMode){
21420             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21421                 v.setVisibilityMode(Roo.Element.DISPLAY);
21422                 v.hide();
21423             });
21424             
21425             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21426                 v.setStyle('width', '189px');
21427             });
21428         }
21429         
21430         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21431             if(!this.calendarWeeks){
21432                 v.remove();
21433                 return;
21434             }
21435             
21436             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21437             v.attr('colspan', function(i, val){
21438                 return parseInt(val) + 1;
21439             });
21440         });
21441                         
21442         
21443         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21444         
21445         this.setStartDate(this.startDate);
21446         this.setEndDate(this.endDate);
21447         
21448         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21449         
21450         this.fillDow();
21451         this.fillMonths();
21452         this.update();
21453         this.showMode();
21454         
21455         if(this.isInline) {
21456             this.showPopup();
21457         }
21458     },
21459     
21460     picker : function()
21461     {
21462         return this.pickerEl;
21463 //        return this.el.select('.datepicker', true).first();
21464     },
21465     
21466     fillDow: function()
21467     {
21468         var dowCnt = this.weekStart;
21469         
21470         var dow = {
21471             tag: 'tr',
21472             cn: [
21473                 
21474             ]
21475         };
21476         
21477         if(this.calendarWeeks){
21478             dow.cn.push({
21479                 tag: 'th',
21480                 cls: 'cw',
21481                 html: '&nbsp;'
21482             })
21483         }
21484         
21485         while (dowCnt < this.weekStart + 7) {
21486             dow.cn.push({
21487                 tag: 'th',
21488                 cls: 'dow',
21489                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21490             });
21491         }
21492         
21493         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21494     },
21495     
21496     fillMonths: function()
21497     {    
21498         var i = 0;
21499         var months = this.picker().select('>.datepicker-months td', true).first();
21500         
21501         months.dom.innerHTML = '';
21502         
21503         while (i < 12) {
21504             var month = {
21505                 tag: 'span',
21506                 cls: 'month',
21507                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21508             };
21509             
21510             months.createChild(month);
21511         }
21512         
21513     },
21514     
21515     update: function()
21516     {
21517         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;
21518         
21519         if (this.date < this.startDate) {
21520             this.viewDate = new Date(this.startDate);
21521         } else if (this.date > this.endDate) {
21522             this.viewDate = new Date(this.endDate);
21523         } else {
21524             this.viewDate = new Date(this.date);
21525         }
21526         
21527         this.fill();
21528     },
21529     
21530     fill: function() 
21531     {
21532         var d = new Date(this.viewDate),
21533                 year = d.getUTCFullYear(),
21534                 month = d.getUTCMonth(),
21535                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21536                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21537                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21538                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21539                 currentDate = this.date && this.date.valueOf(),
21540                 today = this.UTCToday();
21541         
21542         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21543         
21544 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21545         
21546 //        this.picker.select('>tfoot th.today').
21547 //                                              .text(dates[this.language].today)
21548 //                                              .toggle(this.todayBtn !== false);
21549     
21550         this.updateNavArrows();
21551         this.fillMonths();
21552                                                 
21553         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21554         
21555         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21556          
21557         prevMonth.setUTCDate(day);
21558         
21559         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21560         
21561         var nextMonth = new Date(prevMonth);
21562         
21563         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21564         
21565         nextMonth = nextMonth.valueOf();
21566         
21567         var fillMonths = false;
21568         
21569         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21570         
21571         while(prevMonth.valueOf() <= nextMonth) {
21572             var clsName = '';
21573             
21574             if (prevMonth.getUTCDay() === this.weekStart) {
21575                 if(fillMonths){
21576                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21577                 }
21578                     
21579                 fillMonths = {
21580                     tag: 'tr',
21581                     cn: []
21582                 };
21583                 
21584                 if(this.calendarWeeks){
21585                     // ISO 8601: First week contains first thursday.
21586                     // ISO also states week starts on Monday, but we can be more abstract here.
21587                     var
21588                     // Start of current week: based on weekstart/current date
21589                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21590                     // Thursday of this week
21591                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21592                     // First Thursday of year, year from thursday
21593                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21594                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21595                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21596                     
21597                     fillMonths.cn.push({
21598                         tag: 'td',
21599                         cls: 'cw',
21600                         html: calWeek
21601                     });
21602                 }
21603             }
21604             
21605             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21606                 clsName += ' old';
21607             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21608                 clsName += ' new';
21609             }
21610             if (this.todayHighlight &&
21611                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21612                 prevMonth.getUTCMonth() == today.getMonth() &&
21613                 prevMonth.getUTCDate() == today.getDate()) {
21614                 clsName += ' today';
21615             }
21616             
21617             if (currentDate && prevMonth.valueOf() === currentDate) {
21618                 clsName += ' active';
21619             }
21620             
21621             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21622                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21623                     clsName += ' disabled';
21624             }
21625             
21626             fillMonths.cn.push({
21627                 tag: 'td',
21628                 cls: 'day ' + clsName,
21629                 html: prevMonth.getDate()
21630             });
21631             
21632             prevMonth.setDate(prevMonth.getDate()+1);
21633         }
21634           
21635         var currentYear = this.date && this.date.getUTCFullYear();
21636         var currentMonth = this.date && this.date.getUTCMonth();
21637         
21638         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21639         
21640         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21641             v.removeClass('active');
21642             
21643             if(currentYear === year && k === currentMonth){
21644                 v.addClass('active');
21645             }
21646             
21647             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21648                 v.addClass('disabled');
21649             }
21650             
21651         });
21652         
21653         
21654         year = parseInt(year/10, 10) * 10;
21655         
21656         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21657         
21658         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21659         
21660         year -= 1;
21661         for (var i = -1; i < 11; i++) {
21662             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21663                 tag: 'span',
21664                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21665                 html: year
21666             });
21667             
21668             year += 1;
21669         }
21670     },
21671     
21672     showMode: function(dir) 
21673     {
21674         if (dir) {
21675             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21676         }
21677         
21678         Roo.each(this.picker().select('>div',true).elements, function(v){
21679             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21680             v.hide();
21681         });
21682         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21683     },
21684     
21685     place: function()
21686     {
21687         if(this.isInline) {
21688             return;
21689         }
21690         
21691         this.picker().removeClass(['bottom', 'top']);
21692         
21693         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21694             /*
21695              * place to the top of element!
21696              *
21697              */
21698             
21699             this.picker().addClass('top');
21700             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21701             
21702             return;
21703         }
21704         
21705         this.picker().addClass('bottom');
21706         
21707         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21708     },
21709     
21710     parseDate : function(value)
21711     {
21712         if(!value || value instanceof Date){
21713             return value;
21714         }
21715         var v = Date.parseDate(value, this.format);
21716         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21717             v = Date.parseDate(value, 'Y-m-d');
21718         }
21719         if(!v && this.altFormats){
21720             if(!this.altFormatsArray){
21721                 this.altFormatsArray = this.altFormats.split("|");
21722             }
21723             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21724                 v = Date.parseDate(value, this.altFormatsArray[i]);
21725             }
21726         }
21727         return v;
21728     },
21729     
21730     formatDate : function(date, fmt)
21731     {   
21732         return (!date || !(date instanceof Date)) ?
21733         date : date.dateFormat(fmt || this.format);
21734     },
21735     
21736     onFocus : function()
21737     {
21738         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21739         this.showPopup();
21740     },
21741     
21742     onBlur : function()
21743     {
21744         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21745         
21746         var d = this.inputEl().getValue();
21747         
21748         this.setValue(d);
21749                 
21750         this.hidePopup();
21751     },
21752     
21753     showPopup : function()
21754     {
21755         this.picker().show();
21756         this.update();
21757         this.place();
21758         
21759         this.fireEvent('showpopup', this, this.date);
21760     },
21761     
21762     hidePopup : function()
21763     {
21764         if(this.isInline) {
21765             return;
21766         }
21767         this.picker().hide();
21768         this.viewMode = this.startViewMode;
21769         this.showMode();
21770         
21771         this.fireEvent('hidepopup', this, this.date);
21772         
21773     },
21774     
21775     onMousedown: function(e)
21776     {
21777         e.stopPropagation();
21778         e.preventDefault();
21779     },
21780     
21781     keyup: function(e)
21782     {
21783         Roo.bootstrap.DateField.superclass.keyup.call(this);
21784         this.update();
21785     },
21786
21787     setValue: function(v)
21788     {
21789         if(this.fireEvent('beforeselect', this, v) !== false){
21790             var d = new Date(this.parseDate(v) ).clearTime();
21791         
21792             if(isNaN(d.getTime())){
21793                 this.date = this.viewDate = '';
21794                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21795                 return;
21796             }
21797
21798             v = this.formatDate(d);
21799
21800             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21801
21802             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21803
21804             this.update();
21805
21806             this.fireEvent('select', this, this.date);
21807         }
21808     },
21809     
21810     getValue: function()
21811     {
21812         return this.formatDate(this.date);
21813     },
21814     
21815     fireKey: function(e)
21816     {
21817         if (!this.picker().isVisible()){
21818             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21819                 this.showPopup();
21820             }
21821             return;
21822         }
21823         
21824         var dateChanged = false,
21825         dir, day, month,
21826         newDate, newViewDate;
21827         
21828         switch(e.keyCode){
21829             case 27: // escape
21830                 this.hidePopup();
21831                 e.preventDefault();
21832                 break;
21833             case 37: // left
21834             case 39: // right
21835                 if (!this.keyboardNavigation) {
21836                     break;
21837                 }
21838                 dir = e.keyCode == 37 ? -1 : 1;
21839                 
21840                 if (e.ctrlKey){
21841                     newDate = this.moveYear(this.date, dir);
21842                     newViewDate = this.moveYear(this.viewDate, dir);
21843                 } else if (e.shiftKey){
21844                     newDate = this.moveMonth(this.date, dir);
21845                     newViewDate = this.moveMonth(this.viewDate, dir);
21846                 } else {
21847                     newDate = new Date(this.date);
21848                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21849                     newViewDate = new Date(this.viewDate);
21850                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21851                 }
21852                 if (this.dateWithinRange(newDate)){
21853                     this.date = newDate;
21854                     this.viewDate = newViewDate;
21855                     this.setValue(this.formatDate(this.date));
21856 //                    this.update();
21857                     e.preventDefault();
21858                     dateChanged = true;
21859                 }
21860                 break;
21861             case 38: // up
21862             case 40: // down
21863                 if (!this.keyboardNavigation) {
21864                     break;
21865                 }
21866                 dir = e.keyCode == 38 ? -1 : 1;
21867                 if (e.ctrlKey){
21868                     newDate = this.moveYear(this.date, dir);
21869                     newViewDate = this.moveYear(this.viewDate, dir);
21870                 } else if (e.shiftKey){
21871                     newDate = this.moveMonth(this.date, dir);
21872                     newViewDate = this.moveMonth(this.viewDate, dir);
21873                 } else {
21874                     newDate = new Date(this.date);
21875                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21876                     newViewDate = new Date(this.viewDate);
21877                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21878                 }
21879                 if (this.dateWithinRange(newDate)){
21880                     this.date = newDate;
21881                     this.viewDate = newViewDate;
21882                     this.setValue(this.formatDate(this.date));
21883 //                    this.update();
21884                     e.preventDefault();
21885                     dateChanged = true;
21886                 }
21887                 break;
21888             case 13: // enter
21889                 this.setValue(this.formatDate(this.date));
21890                 this.hidePopup();
21891                 e.preventDefault();
21892                 break;
21893             case 9: // tab
21894                 this.setValue(this.formatDate(this.date));
21895                 this.hidePopup();
21896                 break;
21897             case 16: // shift
21898             case 17: // ctrl
21899             case 18: // alt
21900                 break;
21901             default :
21902                 this.hidePopup();
21903                 
21904         }
21905     },
21906     
21907     
21908     onClick: function(e) 
21909     {
21910         e.stopPropagation();
21911         e.preventDefault();
21912         
21913         var target = e.getTarget();
21914         
21915         if(target.nodeName.toLowerCase() === 'i'){
21916             target = Roo.get(target).dom.parentNode;
21917         }
21918         
21919         var nodeName = target.nodeName;
21920         var className = target.className;
21921         var html = target.innerHTML;
21922         //Roo.log(nodeName);
21923         
21924         switch(nodeName.toLowerCase()) {
21925             case 'th':
21926                 switch(className) {
21927                     case 'switch':
21928                         this.showMode(1);
21929                         break;
21930                     case 'prev':
21931                     case 'next':
21932                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21933                         switch(this.viewMode){
21934                                 case 0:
21935                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21936                                         break;
21937                                 case 1:
21938                                 case 2:
21939                                         this.viewDate = this.moveYear(this.viewDate, dir);
21940                                         break;
21941                         }
21942                         this.fill();
21943                         break;
21944                     case 'today':
21945                         var date = new Date();
21946                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21947 //                        this.fill()
21948                         this.setValue(this.formatDate(this.date));
21949                         
21950                         this.hidePopup();
21951                         break;
21952                 }
21953                 break;
21954             case 'span':
21955                 if (className.indexOf('disabled') < 0) {
21956                 if (!this.viewDate) {
21957                     this.viewDate = new Date();
21958                 }
21959                 this.viewDate.setUTCDate(1);
21960                     if (className.indexOf('month') > -1) {
21961                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21962                     } else {
21963                         var year = parseInt(html, 10) || 0;
21964                         this.viewDate.setUTCFullYear(year);
21965                         
21966                     }
21967                     
21968                     if(this.singleMode){
21969                         this.setValue(this.formatDate(this.viewDate));
21970                         this.hidePopup();
21971                         return;
21972                     }
21973                     
21974                     this.showMode(-1);
21975                     this.fill();
21976                 }
21977                 break;
21978                 
21979             case 'td':
21980                 //Roo.log(className);
21981                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21982                     var day = parseInt(html, 10) || 1;
21983                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21984                         month = (this.viewDate || new Date()).getUTCMonth();
21985
21986                     if (className.indexOf('old') > -1) {
21987                         if(month === 0 ){
21988                             month = 11;
21989                             year -= 1;
21990                         }else{
21991                             month -= 1;
21992                         }
21993                     } else if (className.indexOf('new') > -1) {
21994                         if (month == 11) {
21995                             month = 0;
21996                             year += 1;
21997                         } else {
21998                             month += 1;
21999                         }
22000                     }
22001                     //Roo.log([year,month,day]);
22002                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22003                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22004 //                    this.fill();
22005                     //Roo.log(this.formatDate(this.date));
22006                     this.setValue(this.formatDate(this.date));
22007                     this.hidePopup();
22008                 }
22009                 break;
22010         }
22011     },
22012     
22013     setStartDate: function(startDate)
22014     {
22015         this.startDate = startDate || -Infinity;
22016         if (this.startDate !== -Infinity) {
22017             this.startDate = this.parseDate(this.startDate);
22018         }
22019         this.update();
22020         this.updateNavArrows();
22021     },
22022
22023     setEndDate: function(endDate)
22024     {
22025         this.endDate = endDate || Infinity;
22026         if (this.endDate !== Infinity) {
22027             this.endDate = this.parseDate(this.endDate);
22028         }
22029         this.update();
22030         this.updateNavArrows();
22031     },
22032     
22033     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22034     {
22035         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22036         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22037             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22038         }
22039         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22040             return parseInt(d, 10);
22041         });
22042         this.update();
22043         this.updateNavArrows();
22044     },
22045     
22046     updateNavArrows: function() 
22047     {
22048         if(this.singleMode){
22049             return;
22050         }
22051         
22052         var d = new Date(this.viewDate),
22053         year = d.getUTCFullYear(),
22054         month = d.getUTCMonth();
22055         
22056         Roo.each(this.picker().select('.prev', true).elements, function(v){
22057             v.show();
22058             switch (this.viewMode) {
22059                 case 0:
22060
22061                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22062                         v.hide();
22063                     }
22064                     break;
22065                 case 1:
22066                 case 2:
22067                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22068                         v.hide();
22069                     }
22070                     break;
22071             }
22072         });
22073         
22074         Roo.each(this.picker().select('.next', true).elements, function(v){
22075             v.show();
22076             switch (this.viewMode) {
22077                 case 0:
22078
22079                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22080                         v.hide();
22081                     }
22082                     break;
22083                 case 1:
22084                 case 2:
22085                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22086                         v.hide();
22087                     }
22088                     break;
22089             }
22090         })
22091     },
22092     
22093     moveMonth: function(date, dir)
22094     {
22095         if (!dir) {
22096             return date;
22097         }
22098         var new_date = new Date(date.valueOf()),
22099         day = new_date.getUTCDate(),
22100         month = new_date.getUTCMonth(),
22101         mag = Math.abs(dir),
22102         new_month, test;
22103         dir = dir > 0 ? 1 : -1;
22104         if (mag == 1){
22105             test = dir == -1
22106             // If going back one month, make sure month is not current month
22107             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22108             ? function(){
22109                 return new_date.getUTCMonth() == month;
22110             }
22111             // If going forward one month, make sure month is as expected
22112             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22113             : function(){
22114                 return new_date.getUTCMonth() != new_month;
22115             };
22116             new_month = month + dir;
22117             new_date.setUTCMonth(new_month);
22118             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22119             if (new_month < 0 || new_month > 11) {
22120                 new_month = (new_month + 12) % 12;
22121             }
22122         } else {
22123             // For magnitudes >1, move one month at a time...
22124             for (var i=0; i<mag; i++) {
22125                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22126                 new_date = this.moveMonth(new_date, dir);
22127             }
22128             // ...then reset the day, keeping it in the new month
22129             new_month = new_date.getUTCMonth();
22130             new_date.setUTCDate(day);
22131             test = function(){
22132                 return new_month != new_date.getUTCMonth();
22133             };
22134         }
22135         // Common date-resetting loop -- if date is beyond end of month, make it
22136         // end of month
22137         while (test()){
22138             new_date.setUTCDate(--day);
22139             new_date.setUTCMonth(new_month);
22140         }
22141         return new_date;
22142     },
22143
22144     moveYear: function(date, dir)
22145     {
22146         return this.moveMonth(date, dir*12);
22147     },
22148
22149     dateWithinRange: function(date)
22150     {
22151         return date >= this.startDate && date <= this.endDate;
22152     },
22153
22154     
22155     remove: function() 
22156     {
22157         this.picker().remove();
22158     },
22159     
22160     validateValue : function(value)
22161     {
22162         if(this.getVisibilityEl().hasClass('hidden')){
22163             return true;
22164         }
22165         
22166         if(value.length < 1)  {
22167             if(this.allowBlank){
22168                 return true;
22169             }
22170             return false;
22171         }
22172         
22173         if(value.length < this.minLength){
22174             return false;
22175         }
22176         if(value.length > this.maxLength){
22177             return false;
22178         }
22179         if(this.vtype){
22180             var vt = Roo.form.VTypes;
22181             if(!vt[this.vtype](value, this)){
22182                 return false;
22183             }
22184         }
22185         if(typeof this.validator == "function"){
22186             var msg = this.validator(value);
22187             if(msg !== true){
22188                 return false;
22189             }
22190         }
22191         
22192         if(this.regex && !this.regex.test(value)){
22193             return false;
22194         }
22195         
22196         if(typeof(this.parseDate(value)) == 'undefined'){
22197             return false;
22198         }
22199         
22200         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22201             return false;
22202         }      
22203         
22204         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22205             return false;
22206         } 
22207         
22208         
22209         return true;
22210     },
22211     
22212     reset : function()
22213     {
22214         this.date = this.viewDate = '';
22215         
22216         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22217     }
22218    
22219 });
22220
22221 Roo.apply(Roo.bootstrap.DateField,  {
22222     
22223     head : {
22224         tag: 'thead',
22225         cn: [
22226         {
22227             tag: 'tr',
22228             cn: [
22229             {
22230                 tag: 'th',
22231                 cls: 'prev',
22232                 html: '<i class="fa fa-arrow-left"/>'
22233             },
22234             {
22235                 tag: 'th',
22236                 cls: 'switch',
22237                 colspan: '5'
22238             },
22239             {
22240                 tag: 'th',
22241                 cls: 'next',
22242                 html: '<i class="fa fa-arrow-right"/>'
22243             }
22244
22245             ]
22246         }
22247         ]
22248     },
22249     
22250     content : {
22251         tag: 'tbody',
22252         cn: [
22253         {
22254             tag: 'tr',
22255             cn: [
22256             {
22257                 tag: 'td',
22258                 colspan: '7'
22259             }
22260             ]
22261         }
22262         ]
22263     },
22264     
22265     footer : {
22266         tag: 'tfoot',
22267         cn: [
22268         {
22269             tag: 'tr',
22270             cn: [
22271             {
22272                 tag: 'th',
22273                 colspan: '7',
22274                 cls: 'today'
22275             }
22276                     
22277             ]
22278         }
22279         ]
22280     },
22281     
22282     dates:{
22283         en: {
22284             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22285             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22286             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22287             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22288             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22289             today: "Today"
22290         }
22291     },
22292     
22293     modes: [
22294     {
22295         clsName: 'days',
22296         navFnc: 'Month',
22297         navStep: 1
22298     },
22299     {
22300         clsName: 'months',
22301         navFnc: 'FullYear',
22302         navStep: 1
22303     },
22304     {
22305         clsName: 'years',
22306         navFnc: 'FullYear',
22307         navStep: 10
22308     }]
22309 });
22310
22311 Roo.apply(Roo.bootstrap.DateField,  {
22312   
22313     template : {
22314         tag: 'div',
22315         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22316         cn: [
22317         {
22318             tag: 'div',
22319             cls: 'datepicker-days',
22320             cn: [
22321             {
22322                 tag: 'table',
22323                 cls: 'table-condensed',
22324                 cn:[
22325                 Roo.bootstrap.DateField.head,
22326                 {
22327                     tag: 'tbody'
22328                 },
22329                 Roo.bootstrap.DateField.footer
22330                 ]
22331             }
22332             ]
22333         },
22334         {
22335             tag: 'div',
22336             cls: 'datepicker-months',
22337             cn: [
22338             {
22339                 tag: 'table',
22340                 cls: 'table-condensed',
22341                 cn:[
22342                 Roo.bootstrap.DateField.head,
22343                 Roo.bootstrap.DateField.content,
22344                 Roo.bootstrap.DateField.footer
22345                 ]
22346             }
22347             ]
22348         },
22349         {
22350             tag: 'div',
22351             cls: 'datepicker-years',
22352             cn: [
22353             {
22354                 tag: 'table',
22355                 cls: 'table-condensed',
22356                 cn:[
22357                 Roo.bootstrap.DateField.head,
22358                 Roo.bootstrap.DateField.content,
22359                 Roo.bootstrap.DateField.footer
22360                 ]
22361             }
22362             ]
22363         }
22364         ]
22365     }
22366 });
22367
22368  
22369
22370  /*
22371  * - LGPL
22372  *
22373  * TimeField
22374  * 
22375  */
22376
22377 /**
22378  * @class Roo.bootstrap.TimeField
22379  * @extends Roo.bootstrap.Input
22380  * Bootstrap DateField class
22381  * 
22382  * 
22383  * @constructor
22384  * Create a new TimeField
22385  * @param {Object} config The config object
22386  */
22387
22388 Roo.bootstrap.TimeField = function(config){
22389     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22390     this.addEvents({
22391             /**
22392              * @event show
22393              * Fires when this field show.
22394              * @param {Roo.bootstrap.DateField} thisthis
22395              * @param {Mixed} date The date value
22396              */
22397             show : true,
22398             /**
22399              * @event show
22400              * Fires when this field hide.
22401              * @param {Roo.bootstrap.DateField} this
22402              * @param {Mixed} date The date value
22403              */
22404             hide : true,
22405             /**
22406              * @event select
22407              * Fires when select a date.
22408              * @param {Roo.bootstrap.DateField} this
22409              * @param {Mixed} date The date value
22410              */
22411             select : true
22412         });
22413 };
22414
22415 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22416     
22417     /**
22418      * @cfg {String} format
22419      * The default time format string which can be overriden for localization support.  The format must be
22420      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22421      */
22422     format : "H:i",
22423
22424     getAutoCreate : function()
22425     {
22426         this.after = '<i class="fa far fa-clock"></i>';
22427         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22428         
22429          
22430     },
22431     onRender: function(ct, position)
22432     {
22433         
22434         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22435                 
22436         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22437         
22438         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22439         
22440         this.pop = this.picker().select('>.datepicker-time',true).first();
22441         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22442         
22443         this.picker().on('mousedown', this.onMousedown, this);
22444         this.picker().on('click', this.onClick, this);
22445         
22446         this.picker().addClass('datepicker-dropdown');
22447     
22448         this.fillTime();
22449         this.update();
22450             
22451         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22452         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22453         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22454         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22455         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22456         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22457
22458     },
22459     
22460     fireKey: function(e){
22461         if (!this.picker().isVisible()){
22462             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22463                 this.show();
22464             }
22465             return;
22466         }
22467
22468         e.preventDefault();
22469         
22470         switch(e.keyCode){
22471             case 27: // escape
22472                 this.hide();
22473                 break;
22474             case 37: // left
22475             case 39: // right
22476                 this.onTogglePeriod();
22477                 break;
22478             case 38: // up
22479                 this.onIncrementMinutes();
22480                 break;
22481             case 40: // down
22482                 this.onDecrementMinutes();
22483                 break;
22484             case 13: // enter
22485             case 9: // tab
22486                 this.setTime();
22487                 break;
22488         }
22489     },
22490     
22491     onClick: function(e) {
22492         e.stopPropagation();
22493         e.preventDefault();
22494     },
22495     
22496     picker : function()
22497     {
22498         return this.pickerEl;
22499     },
22500     
22501     fillTime: function()
22502     {    
22503         var time = this.pop.select('tbody', true).first();
22504         
22505         time.dom.innerHTML = '';
22506         
22507         time.createChild({
22508             tag: 'tr',
22509             cn: [
22510                 {
22511                     tag: 'td',
22512                     cn: [
22513                         {
22514                             tag: 'a',
22515                             href: '#',
22516                             cls: 'btn',
22517                             cn: [
22518                                 {
22519                                     tag: 'i',
22520                                     cls: 'hours-up fa fas fa-chevron-up'
22521                                 }
22522                             ]
22523                         } 
22524                     ]
22525                 },
22526                 {
22527                     tag: 'td',
22528                     cls: 'separator'
22529                 },
22530                 {
22531                     tag: 'td',
22532                     cn: [
22533                         {
22534                             tag: 'a',
22535                             href: '#',
22536                             cls: 'btn',
22537                             cn: [
22538                                 {
22539                                     tag: 'i',
22540                                     cls: 'minutes-up fa fas fa-chevron-up'
22541                                 }
22542                             ]
22543                         }
22544                     ]
22545                 },
22546                 {
22547                     tag: 'td',
22548                     cls: 'separator'
22549                 }
22550             ]
22551         });
22552         
22553         time.createChild({
22554             tag: 'tr',
22555             cn: [
22556                 {
22557                     tag: 'td',
22558                     cn: [
22559                         {
22560                             tag: 'span',
22561                             cls: 'timepicker-hour',
22562                             html: '00'
22563                         }  
22564                     ]
22565                 },
22566                 {
22567                     tag: 'td',
22568                     cls: 'separator',
22569                     html: ':'
22570                 },
22571                 {
22572                     tag: 'td',
22573                     cn: [
22574                         {
22575                             tag: 'span',
22576                             cls: 'timepicker-minute',
22577                             html: '00'
22578                         }  
22579                     ]
22580                 },
22581                 {
22582                     tag: 'td',
22583                     cls: 'separator'
22584                 },
22585                 {
22586                     tag: 'td',
22587                     cn: [
22588                         {
22589                             tag: 'button',
22590                             type: 'button',
22591                             cls: 'btn btn-primary period',
22592                             html: 'AM'
22593                             
22594                         }
22595                     ]
22596                 }
22597             ]
22598         });
22599         
22600         time.createChild({
22601             tag: 'tr',
22602             cn: [
22603                 {
22604                     tag: 'td',
22605                     cn: [
22606                         {
22607                             tag: 'a',
22608                             href: '#',
22609                             cls: 'btn',
22610                             cn: [
22611                                 {
22612                                     tag: 'span',
22613                                     cls: 'hours-down fa fas fa-chevron-down'
22614                                 }
22615                             ]
22616                         }
22617                     ]
22618                 },
22619                 {
22620                     tag: 'td',
22621                     cls: 'separator'
22622                 },
22623                 {
22624                     tag: 'td',
22625                     cn: [
22626                         {
22627                             tag: 'a',
22628                             href: '#',
22629                             cls: 'btn',
22630                             cn: [
22631                                 {
22632                                     tag: 'span',
22633                                     cls: 'minutes-down fa fas fa-chevron-down'
22634                                 }
22635                             ]
22636                         }
22637                     ]
22638                 },
22639                 {
22640                     tag: 'td',
22641                     cls: 'separator'
22642                 }
22643             ]
22644         });
22645         
22646     },
22647     
22648     update: function()
22649     {
22650         
22651         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22652         
22653         this.fill();
22654     },
22655     
22656     fill: function() 
22657     {
22658         var hours = this.time.getHours();
22659         var minutes = this.time.getMinutes();
22660         var period = 'AM';
22661         
22662         if(hours > 11){
22663             period = 'PM';
22664         }
22665         
22666         if(hours == 0){
22667             hours = 12;
22668         }
22669         
22670         
22671         if(hours > 12){
22672             hours = hours - 12;
22673         }
22674         
22675         if(hours < 10){
22676             hours = '0' + hours;
22677         }
22678         
22679         if(minutes < 10){
22680             minutes = '0' + minutes;
22681         }
22682         
22683         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22684         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22685         this.pop.select('button', true).first().dom.innerHTML = period;
22686         
22687     },
22688     
22689     place: function()
22690     {   
22691         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22692         
22693         var cls = ['bottom'];
22694         
22695         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22696             cls.pop();
22697             cls.push('top');
22698         }
22699         
22700         cls.push('right');
22701         
22702         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22703             cls.pop();
22704             cls.push('left');
22705         }
22706         //this.picker().setXY(20000,20000);
22707         this.picker().addClass(cls.join('-'));
22708         
22709         var _this = this;
22710         
22711         Roo.each(cls, function(c){
22712             if(c == 'bottom'){
22713                 (function() {
22714                  //  
22715                 }).defer(200);
22716                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22717                 //_this.picker().setTop(_this.inputEl().getHeight());
22718                 return;
22719             }
22720             if(c == 'top'){
22721                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22722                 
22723                 //_this.picker().setTop(0 - _this.picker().getHeight());
22724                 return;
22725             }
22726             /*
22727             if(c == 'left'){
22728                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22729                 return;
22730             }
22731             if(c == 'right'){
22732                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22733                 return;
22734             }
22735             */
22736         });
22737         
22738     },
22739   
22740     onFocus : function()
22741     {
22742         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22743         this.show();
22744     },
22745     
22746     onBlur : function()
22747     {
22748         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22749         this.hide();
22750     },
22751     
22752     show : function()
22753     {
22754         this.picker().show();
22755         this.pop.show();
22756         this.update();
22757         this.place();
22758         
22759         this.fireEvent('show', this, this.date);
22760     },
22761     
22762     hide : function()
22763     {
22764         this.picker().hide();
22765         this.pop.hide();
22766         
22767         this.fireEvent('hide', this, this.date);
22768     },
22769     
22770     setTime : function()
22771     {
22772         this.hide();
22773         this.setValue(this.time.format(this.format));
22774         
22775         this.fireEvent('select', this, this.date);
22776         
22777         
22778     },
22779     
22780     onMousedown: function(e){
22781         e.stopPropagation();
22782         e.preventDefault();
22783     },
22784     
22785     onIncrementHours: function()
22786     {
22787         Roo.log('onIncrementHours');
22788         this.time = this.time.add(Date.HOUR, 1);
22789         this.update();
22790         
22791     },
22792     
22793     onDecrementHours: function()
22794     {
22795         Roo.log('onDecrementHours');
22796         this.time = this.time.add(Date.HOUR, -1);
22797         this.update();
22798     },
22799     
22800     onIncrementMinutes: function()
22801     {
22802         Roo.log('onIncrementMinutes');
22803         this.time = this.time.add(Date.MINUTE, 1);
22804         this.update();
22805     },
22806     
22807     onDecrementMinutes: function()
22808     {
22809         Roo.log('onDecrementMinutes');
22810         this.time = this.time.add(Date.MINUTE, -1);
22811         this.update();
22812     },
22813     
22814     onTogglePeriod: function()
22815     {
22816         Roo.log('onTogglePeriod');
22817         this.time = this.time.add(Date.HOUR, 12);
22818         this.update();
22819     }
22820     
22821    
22822 });
22823  
22824
22825 Roo.apply(Roo.bootstrap.TimeField,  {
22826   
22827     template : {
22828         tag: 'div',
22829         cls: 'datepicker dropdown-menu',
22830         cn: [
22831             {
22832                 tag: 'div',
22833                 cls: 'datepicker-time',
22834                 cn: [
22835                 {
22836                     tag: 'table',
22837                     cls: 'table-condensed',
22838                     cn:[
22839                         {
22840                             tag: 'tbody',
22841                             cn: [
22842                                 {
22843                                     tag: 'tr',
22844                                     cn: [
22845                                     {
22846                                         tag: 'td',
22847                                         colspan: '7'
22848                                     }
22849                                     ]
22850                                 }
22851                             ]
22852                         },
22853                         {
22854                             tag: 'tfoot',
22855                             cn: [
22856                                 {
22857                                     tag: 'tr',
22858                                     cn: [
22859                                     {
22860                                         tag: 'th',
22861                                         colspan: '7',
22862                                         cls: '',
22863                                         cn: [
22864                                             {
22865                                                 tag: 'button',
22866                                                 cls: 'btn btn-info ok',
22867                                                 html: 'OK'
22868                                             }
22869                                         ]
22870                                     }
22871                     
22872                                     ]
22873                                 }
22874                             ]
22875                         }
22876                     ]
22877                 }
22878                 ]
22879             }
22880         ]
22881     }
22882 });
22883
22884  
22885
22886  /*
22887  * - LGPL
22888  *
22889  * MonthField
22890  * 
22891  */
22892
22893 /**
22894  * @class Roo.bootstrap.MonthField
22895  * @extends Roo.bootstrap.Input
22896  * Bootstrap MonthField class
22897  * 
22898  * @cfg {String} language default en
22899  * 
22900  * @constructor
22901  * Create a new MonthField
22902  * @param {Object} config The config object
22903  */
22904
22905 Roo.bootstrap.MonthField = function(config){
22906     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22907     
22908     this.addEvents({
22909         /**
22910          * @event show
22911          * Fires when this field show.
22912          * @param {Roo.bootstrap.MonthField} this
22913          * @param {Mixed} date The date value
22914          */
22915         show : true,
22916         /**
22917          * @event show
22918          * Fires when this field hide.
22919          * @param {Roo.bootstrap.MonthField} this
22920          * @param {Mixed} date The date value
22921          */
22922         hide : true,
22923         /**
22924          * @event select
22925          * Fires when select a date.
22926          * @param {Roo.bootstrap.MonthField} this
22927          * @param {String} oldvalue The old value
22928          * @param {String} newvalue The new value
22929          */
22930         select : true
22931     });
22932 };
22933
22934 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22935     
22936     onRender: function(ct, position)
22937     {
22938         
22939         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22940         
22941         this.language = this.language || 'en';
22942         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22943         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22944         
22945         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22946         this.isInline = false;
22947         this.isInput = true;
22948         this.component = this.el.select('.add-on', true).first() || false;
22949         this.component = (this.component && this.component.length === 0) ? false : this.component;
22950         this.hasInput = this.component && this.inputEL().length;
22951         
22952         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22953         
22954         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22955         
22956         this.picker().on('mousedown', this.onMousedown, this);
22957         this.picker().on('click', this.onClick, this);
22958         
22959         this.picker().addClass('datepicker-dropdown');
22960         
22961         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22962             v.setStyle('width', '189px');
22963         });
22964         
22965         this.fillMonths();
22966         
22967         this.update();
22968         
22969         if(this.isInline) {
22970             this.show();
22971         }
22972         
22973     },
22974     
22975     setValue: function(v, suppressEvent)
22976     {   
22977         var o = this.getValue();
22978         
22979         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22980         
22981         this.update();
22982
22983         if(suppressEvent !== true){
22984             this.fireEvent('select', this, o, v);
22985         }
22986         
22987     },
22988     
22989     getValue: function()
22990     {
22991         return this.value;
22992     },
22993     
22994     onClick: function(e) 
22995     {
22996         e.stopPropagation();
22997         e.preventDefault();
22998         
22999         var target = e.getTarget();
23000         
23001         if(target.nodeName.toLowerCase() === 'i'){
23002             target = Roo.get(target).dom.parentNode;
23003         }
23004         
23005         var nodeName = target.nodeName;
23006         var className = target.className;
23007         var html = target.innerHTML;
23008         
23009         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23010             return;
23011         }
23012         
23013         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23014         
23015         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23016         
23017         this.hide();
23018                         
23019     },
23020     
23021     picker : function()
23022     {
23023         return this.pickerEl;
23024     },
23025     
23026     fillMonths: function()
23027     {    
23028         var i = 0;
23029         var months = this.picker().select('>.datepicker-months td', true).first();
23030         
23031         months.dom.innerHTML = '';
23032         
23033         while (i < 12) {
23034             var month = {
23035                 tag: 'span',
23036                 cls: 'month',
23037                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23038             };
23039             
23040             months.createChild(month);
23041         }
23042         
23043     },
23044     
23045     update: function()
23046     {
23047         var _this = this;
23048         
23049         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23050             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23051         }
23052         
23053         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23054             e.removeClass('active');
23055             
23056             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23057                 e.addClass('active');
23058             }
23059         })
23060     },
23061     
23062     place: function()
23063     {
23064         if(this.isInline) {
23065             return;
23066         }
23067         
23068         this.picker().removeClass(['bottom', 'top']);
23069         
23070         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23071             /*
23072              * place to the top of element!
23073              *
23074              */
23075             
23076             this.picker().addClass('top');
23077             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23078             
23079             return;
23080         }
23081         
23082         this.picker().addClass('bottom');
23083         
23084         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23085     },
23086     
23087     onFocus : function()
23088     {
23089         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23090         this.show();
23091     },
23092     
23093     onBlur : function()
23094     {
23095         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23096         
23097         var d = this.inputEl().getValue();
23098         
23099         this.setValue(d);
23100                 
23101         this.hide();
23102     },
23103     
23104     show : function()
23105     {
23106         this.picker().show();
23107         this.picker().select('>.datepicker-months', true).first().show();
23108         this.update();
23109         this.place();
23110         
23111         this.fireEvent('show', this, this.date);
23112     },
23113     
23114     hide : function()
23115     {
23116         if(this.isInline) {
23117             return;
23118         }
23119         this.picker().hide();
23120         this.fireEvent('hide', this, this.date);
23121         
23122     },
23123     
23124     onMousedown: function(e)
23125     {
23126         e.stopPropagation();
23127         e.preventDefault();
23128     },
23129     
23130     keyup: function(e)
23131     {
23132         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23133         this.update();
23134     },
23135
23136     fireKey: function(e)
23137     {
23138         if (!this.picker().isVisible()){
23139             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23140                 this.show();
23141             }
23142             return;
23143         }
23144         
23145         var dir;
23146         
23147         switch(e.keyCode){
23148             case 27: // escape
23149                 this.hide();
23150                 e.preventDefault();
23151                 break;
23152             case 37: // left
23153             case 39: // right
23154                 dir = e.keyCode == 37 ? -1 : 1;
23155                 
23156                 this.vIndex = this.vIndex + dir;
23157                 
23158                 if(this.vIndex < 0){
23159                     this.vIndex = 0;
23160                 }
23161                 
23162                 if(this.vIndex > 11){
23163                     this.vIndex = 11;
23164                 }
23165                 
23166                 if(isNaN(this.vIndex)){
23167                     this.vIndex = 0;
23168                 }
23169                 
23170                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23171                 
23172                 break;
23173             case 38: // up
23174             case 40: // down
23175                 
23176                 dir = e.keyCode == 38 ? -1 : 1;
23177                 
23178                 this.vIndex = this.vIndex + dir * 4;
23179                 
23180                 if(this.vIndex < 0){
23181                     this.vIndex = 0;
23182                 }
23183                 
23184                 if(this.vIndex > 11){
23185                     this.vIndex = 11;
23186                 }
23187                 
23188                 if(isNaN(this.vIndex)){
23189                     this.vIndex = 0;
23190                 }
23191                 
23192                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23193                 break;
23194                 
23195             case 13: // enter
23196                 
23197                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23198                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23199                 }
23200                 
23201                 this.hide();
23202                 e.preventDefault();
23203                 break;
23204             case 9: // tab
23205                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23206                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23207                 }
23208                 this.hide();
23209                 break;
23210             case 16: // shift
23211             case 17: // ctrl
23212             case 18: // alt
23213                 break;
23214             default :
23215                 this.hide();
23216                 
23217         }
23218     },
23219     
23220     remove: function() 
23221     {
23222         this.picker().remove();
23223     }
23224    
23225 });
23226
23227 Roo.apply(Roo.bootstrap.MonthField,  {
23228     
23229     content : {
23230         tag: 'tbody',
23231         cn: [
23232         {
23233             tag: 'tr',
23234             cn: [
23235             {
23236                 tag: 'td',
23237                 colspan: '7'
23238             }
23239             ]
23240         }
23241         ]
23242     },
23243     
23244     dates:{
23245         en: {
23246             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23247             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23248         }
23249     }
23250 });
23251
23252 Roo.apply(Roo.bootstrap.MonthField,  {
23253   
23254     template : {
23255         tag: 'div',
23256         cls: 'datepicker dropdown-menu roo-dynamic',
23257         cn: [
23258             {
23259                 tag: 'div',
23260                 cls: 'datepicker-months',
23261                 cn: [
23262                 {
23263                     tag: 'table',
23264                     cls: 'table-condensed',
23265                     cn:[
23266                         Roo.bootstrap.DateField.content
23267                     ]
23268                 }
23269                 ]
23270             }
23271         ]
23272     }
23273 });
23274
23275  
23276
23277  
23278  /*
23279  * - LGPL
23280  *
23281  * CheckBox
23282  * 
23283  */
23284
23285 /**
23286  * @class Roo.bootstrap.CheckBox
23287  * @extends Roo.bootstrap.Input
23288  * Bootstrap CheckBox class
23289  * 
23290  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23291  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23292  * @cfg {String} boxLabel The text that appears beside the checkbox
23293  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23294  * @cfg {Boolean} checked initnal the element
23295  * @cfg {Boolean} inline inline the element (default false)
23296  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23297  * @cfg {String} tooltip label tooltip
23298  * 
23299  * @constructor
23300  * Create a new CheckBox
23301  * @param {Object} config The config object
23302  */
23303
23304 Roo.bootstrap.CheckBox = function(config){
23305     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23306    
23307     this.addEvents({
23308         /**
23309         * @event check
23310         * Fires when the element is checked or unchecked.
23311         * @param {Roo.bootstrap.CheckBox} this This input
23312         * @param {Boolean} checked The new checked value
23313         */
23314        check : true,
23315        /**
23316         * @event click
23317         * Fires when the element is click.
23318         * @param {Roo.bootstrap.CheckBox} this This input
23319         */
23320        click : true
23321     });
23322     
23323 };
23324
23325 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23326   
23327     inputType: 'checkbox',
23328     inputValue: 1,
23329     valueOff: 0,
23330     boxLabel: false,
23331     checked: false,
23332     weight : false,
23333     inline: false,
23334     tooltip : '',
23335     
23336     // checkbox success does not make any sense really.. 
23337     invalidClass : "",
23338     validClass : "",
23339     
23340     
23341     getAutoCreate : function()
23342     {
23343         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23344         
23345         var id = Roo.id();
23346         
23347         var cfg = {};
23348         
23349         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23350         
23351         if(this.inline){
23352             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23353         }
23354         
23355         var input =  {
23356             tag: 'input',
23357             id : id,
23358             type : this.inputType,
23359             value : this.inputValue,
23360             cls : 'roo-' + this.inputType, //'form-box',
23361             placeholder : this.placeholder || ''
23362             
23363         };
23364         
23365         if(this.inputType != 'radio'){
23366             var hidden =  {
23367                 tag: 'input',
23368                 type : 'hidden',
23369                 cls : 'roo-hidden-value',
23370                 value : this.checked ? this.inputValue : this.valueOff
23371             };
23372         }
23373         
23374             
23375         if (this.weight) { // Validity check?
23376             cfg.cls += " " + this.inputType + "-" + this.weight;
23377         }
23378         
23379         if (this.disabled) {
23380             input.disabled=true;
23381         }
23382         
23383         if(this.checked){
23384             input.checked = this.checked;
23385         }
23386         
23387         if (this.name) {
23388             
23389             input.name = this.name;
23390             
23391             if(this.inputType != 'radio'){
23392                 hidden.name = this.name;
23393                 input.name = '_hidden_' + this.name;
23394             }
23395         }
23396         
23397         if (this.size) {
23398             input.cls += ' input-' + this.size;
23399         }
23400         
23401         var settings=this;
23402         
23403         ['xs','sm','md','lg'].map(function(size){
23404             if (settings[size]) {
23405                 cfg.cls += ' col-' + size + '-' + settings[size];
23406             }
23407         });
23408         
23409         var inputblock = input;
23410          
23411         if (this.before || this.after) {
23412             
23413             inputblock = {
23414                 cls : 'input-group',
23415                 cn :  [] 
23416             };
23417             
23418             if (this.before) {
23419                 inputblock.cn.push({
23420                     tag :'span',
23421                     cls : 'input-group-addon',
23422                     html : this.before
23423                 });
23424             }
23425             
23426             inputblock.cn.push(input);
23427             
23428             if(this.inputType != 'radio'){
23429                 inputblock.cn.push(hidden);
23430             }
23431             
23432             if (this.after) {
23433                 inputblock.cn.push({
23434                     tag :'span',
23435                     cls : 'input-group-addon',
23436                     html : this.after
23437                 });
23438             }
23439             
23440         }
23441         var boxLabelCfg = false;
23442         
23443         if(this.boxLabel){
23444            
23445             boxLabelCfg = {
23446                 tag: 'label',
23447                 //'for': id, // box label is handled by onclick - so no for...
23448                 cls: 'box-label',
23449                 html: this.boxLabel
23450             };
23451             if(this.tooltip){
23452                 boxLabelCfg.tooltip = this.tooltip;
23453             }
23454              
23455         }
23456         
23457         
23458         if (align ==='left' && this.fieldLabel.length) {
23459 //                Roo.log("left and has label");
23460             cfg.cn = [
23461                 {
23462                     tag: 'label',
23463                     'for' :  id,
23464                     cls : 'control-label',
23465                     html : this.fieldLabel
23466                 },
23467                 {
23468                     cls : "", 
23469                     cn: [
23470                         inputblock
23471                     ]
23472                 }
23473             ];
23474             
23475             if (boxLabelCfg) {
23476                 cfg.cn[1].cn.push(boxLabelCfg);
23477             }
23478             
23479             if(this.labelWidth > 12){
23480                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23481             }
23482             
23483             if(this.labelWidth < 13 && this.labelmd == 0){
23484                 this.labelmd = this.labelWidth;
23485             }
23486             
23487             if(this.labellg > 0){
23488                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23489                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23490             }
23491             
23492             if(this.labelmd > 0){
23493                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23494                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23495             }
23496             
23497             if(this.labelsm > 0){
23498                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23499                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23500             }
23501             
23502             if(this.labelxs > 0){
23503                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23504                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23505             }
23506             
23507         } else if ( this.fieldLabel.length) {
23508 //                Roo.log(" label");
23509                 cfg.cn = [
23510                    
23511                     {
23512                         tag: this.boxLabel ? 'span' : 'label',
23513                         'for': id,
23514                         cls: 'control-label box-input-label',
23515                         //cls : 'input-group-addon',
23516                         html : this.fieldLabel
23517                     },
23518                     
23519                     inputblock
23520                     
23521                 ];
23522                 if (boxLabelCfg) {
23523                     cfg.cn.push(boxLabelCfg);
23524                 }
23525
23526         } else {
23527             
23528 //                Roo.log(" no label && no align");
23529                 cfg.cn = [  inputblock ] ;
23530                 if (boxLabelCfg) {
23531                     cfg.cn.push(boxLabelCfg);
23532                 }
23533
23534                 
23535         }
23536         
23537        
23538         
23539         if(this.inputType != 'radio'){
23540             cfg.cn.push(hidden);
23541         }
23542         
23543         return cfg;
23544         
23545     },
23546     
23547     /**
23548      * return the real input element.
23549      */
23550     inputEl: function ()
23551     {
23552         return this.el.select('input.roo-' + this.inputType,true).first();
23553     },
23554     hiddenEl: function ()
23555     {
23556         return this.el.select('input.roo-hidden-value',true).first();
23557     },
23558     
23559     labelEl: function()
23560     {
23561         return this.el.select('label.control-label',true).first();
23562     },
23563     /* depricated... */
23564     
23565     label: function()
23566     {
23567         return this.labelEl();
23568     },
23569     
23570     boxLabelEl: function()
23571     {
23572         return this.el.select('label.box-label',true).first();
23573     },
23574     
23575     initEvents : function()
23576     {
23577 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23578         
23579         this.inputEl().on('click', this.onClick,  this);
23580         
23581         if (this.boxLabel) { 
23582             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23583         }
23584         
23585         this.startValue = this.getValue();
23586         
23587         if(this.groupId){
23588             Roo.bootstrap.CheckBox.register(this);
23589         }
23590     },
23591     
23592     onClick : function(e)
23593     {   
23594         if(this.fireEvent('click', this, e) !== false){
23595             this.setChecked(!this.checked);
23596         }
23597         
23598     },
23599     
23600     setChecked : function(state,suppressEvent)
23601     {
23602         this.startValue = this.getValue();
23603
23604         if(this.inputType == 'radio'){
23605             
23606             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23607                 e.dom.checked = false;
23608             });
23609             
23610             this.inputEl().dom.checked = true;
23611             
23612             this.inputEl().dom.value = this.inputValue;
23613             
23614             if(suppressEvent !== true){
23615                 this.fireEvent('check', this, true);
23616             }
23617             
23618             this.validate();
23619             
23620             return;
23621         }
23622         
23623         this.checked = state;
23624         
23625         this.inputEl().dom.checked = state;
23626         
23627         
23628         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23629         
23630         if(suppressEvent !== true){
23631             this.fireEvent('check', this, state);
23632         }
23633         
23634         this.validate();
23635     },
23636     
23637     getValue : function()
23638     {
23639         if(this.inputType == 'radio'){
23640             return this.getGroupValue();
23641         }
23642         
23643         return this.hiddenEl().dom.value;
23644         
23645     },
23646     
23647     getGroupValue : function()
23648     {
23649         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23650             return '';
23651         }
23652         
23653         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23654     },
23655     
23656     setValue : function(v,suppressEvent)
23657     {
23658         if(this.inputType == 'radio'){
23659             this.setGroupValue(v, suppressEvent);
23660             return;
23661         }
23662         
23663         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23664         
23665         this.validate();
23666     },
23667     
23668     setGroupValue : function(v, suppressEvent)
23669     {
23670         this.startValue = this.getValue();
23671         
23672         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23673             e.dom.checked = false;
23674             
23675             if(e.dom.value == v){
23676                 e.dom.checked = true;
23677             }
23678         });
23679         
23680         if(suppressEvent !== true){
23681             this.fireEvent('check', this, true);
23682         }
23683
23684         this.validate();
23685         
23686         return;
23687     },
23688     
23689     validate : function()
23690     {
23691         if(this.getVisibilityEl().hasClass('hidden')){
23692             return true;
23693         }
23694         
23695         if(
23696                 this.disabled || 
23697                 (this.inputType == 'radio' && this.validateRadio()) ||
23698                 (this.inputType == 'checkbox' && this.validateCheckbox())
23699         ){
23700             this.markValid();
23701             return true;
23702         }
23703         
23704         this.markInvalid();
23705         return false;
23706     },
23707     
23708     validateRadio : function()
23709     {
23710         if(this.getVisibilityEl().hasClass('hidden')){
23711             return true;
23712         }
23713         
23714         if(this.allowBlank){
23715             return true;
23716         }
23717         
23718         var valid = false;
23719         
23720         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23721             if(!e.dom.checked){
23722                 return;
23723             }
23724             
23725             valid = true;
23726             
23727             return false;
23728         });
23729         
23730         return valid;
23731     },
23732     
23733     validateCheckbox : function()
23734     {
23735         if(!this.groupId){
23736             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23737             //return (this.getValue() == this.inputValue) ? true : false;
23738         }
23739         
23740         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23741         
23742         if(!group){
23743             return false;
23744         }
23745         
23746         var r = false;
23747         
23748         for(var i in group){
23749             if(group[i].el.isVisible(true)){
23750                 r = false;
23751                 break;
23752             }
23753             
23754             r = true;
23755         }
23756         
23757         for(var i in group){
23758             if(r){
23759                 break;
23760             }
23761             
23762             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23763         }
23764         
23765         return r;
23766     },
23767     
23768     /**
23769      * Mark this field as valid
23770      */
23771     markValid : function()
23772     {
23773         var _this = this;
23774         
23775         this.fireEvent('valid', this);
23776         
23777         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23778         
23779         if(this.groupId){
23780             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23781         }
23782         
23783         if(label){
23784             label.markValid();
23785         }
23786
23787         if(this.inputType == 'radio'){
23788             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23789                 var fg = e.findParent('.form-group', false, true);
23790                 if (Roo.bootstrap.version == 3) {
23791                     fg.removeClass([_this.invalidClass, _this.validClass]);
23792                     fg.addClass(_this.validClass);
23793                 } else {
23794                     fg.removeClass(['is-valid', 'is-invalid']);
23795                     fg.addClass('is-valid');
23796                 }
23797             });
23798             
23799             return;
23800         }
23801
23802         if(!this.groupId){
23803             var fg = this.el.findParent('.form-group', false, true);
23804             if (Roo.bootstrap.version == 3) {
23805                 fg.removeClass([this.invalidClass, this.validClass]);
23806                 fg.addClass(this.validClass);
23807             } else {
23808                 fg.removeClass(['is-valid', 'is-invalid']);
23809                 fg.addClass('is-valid');
23810             }
23811             return;
23812         }
23813         
23814         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23815         
23816         if(!group){
23817             return;
23818         }
23819         
23820         for(var i in group){
23821             var fg = group[i].el.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     
23832      /**
23833      * Mark this field as invalid
23834      * @param {String} msg The validation message
23835      */
23836     markInvalid : function(msg)
23837     {
23838         if(this.allowBlank){
23839             return;
23840         }
23841         
23842         var _this = this;
23843         
23844         this.fireEvent('invalid', this, msg);
23845         
23846         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23847         
23848         if(this.groupId){
23849             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23850         }
23851         
23852         if(label){
23853             label.markInvalid();
23854         }
23855             
23856         if(this.inputType == 'radio'){
23857             
23858             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23859                 var fg = e.findParent('.form-group', false, true);
23860                 if (Roo.bootstrap.version == 3) {
23861                     fg.removeClass([_this.invalidClass, _this.validClass]);
23862                     fg.addClass(_this.invalidClass);
23863                 } else {
23864                     fg.removeClass(['is-invalid', 'is-valid']);
23865                     fg.addClass('is-invalid');
23866                 }
23867             });
23868             
23869             return;
23870         }
23871         
23872         if(!this.groupId){
23873             var fg = this.el.findParent('.form-group', false, true);
23874             if (Roo.bootstrap.version == 3) {
23875                 fg.removeClass([_this.invalidClass, _this.validClass]);
23876                 fg.addClass(_this.invalidClass);
23877             } else {
23878                 fg.removeClass(['is-invalid', 'is-valid']);
23879                 fg.addClass('is-invalid');
23880             }
23881             return;
23882         }
23883         
23884         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23885         
23886         if(!group){
23887             return;
23888         }
23889         
23890         for(var i in group){
23891             var fg = group[i].el.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     },
23902     
23903     clearInvalid : function()
23904     {
23905         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23906         
23907         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23908         
23909         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23910         
23911         if (label && label.iconEl) {
23912             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23913             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23914         }
23915     },
23916     
23917     disable : function()
23918     {
23919         if(this.inputType != 'radio'){
23920             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23921             return;
23922         }
23923         
23924         var _this = this;
23925         
23926         if(this.rendered){
23927             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23928                 _this.getActionEl().addClass(this.disabledClass);
23929                 e.dom.disabled = true;
23930             });
23931         }
23932         
23933         this.disabled = true;
23934         this.fireEvent("disable", this);
23935         return this;
23936     },
23937
23938     enable : function()
23939     {
23940         if(this.inputType != 'radio'){
23941             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23942             return;
23943         }
23944         
23945         var _this = this;
23946         
23947         if(this.rendered){
23948             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23949                 _this.getActionEl().removeClass(this.disabledClass);
23950                 e.dom.disabled = false;
23951             });
23952         }
23953         
23954         this.disabled = false;
23955         this.fireEvent("enable", this);
23956         return this;
23957     },
23958     
23959     setBoxLabel : function(v)
23960     {
23961         this.boxLabel = v;
23962         
23963         if(this.rendered){
23964             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23965         }
23966     }
23967
23968 });
23969
23970 Roo.apply(Roo.bootstrap.CheckBox, {
23971     
23972     groups: {},
23973     
23974      /**
23975     * register a CheckBox Group
23976     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23977     */
23978     register : function(checkbox)
23979     {
23980         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23981             this.groups[checkbox.groupId] = {};
23982         }
23983         
23984         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23985             return;
23986         }
23987         
23988         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23989         
23990     },
23991     /**
23992     * fetch a CheckBox Group based on the group ID
23993     * @param {string} the group ID
23994     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23995     */
23996     get: function(groupId) {
23997         if (typeof(this.groups[groupId]) == 'undefined') {
23998             return false;
23999         }
24000         
24001         return this.groups[groupId] ;
24002     }
24003     
24004     
24005 });
24006 /*
24007  * - LGPL
24008  *
24009  * RadioItem
24010  * 
24011  */
24012
24013 /**
24014  * @class Roo.bootstrap.Radio
24015  * @extends Roo.bootstrap.Component
24016  * Bootstrap Radio class
24017  * @cfg {String} boxLabel - the label associated
24018  * @cfg {String} value - the value of radio
24019  * 
24020  * @constructor
24021  * Create a new Radio
24022  * @param {Object} config The config object
24023  */
24024 Roo.bootstrap.Radio = function(config){
24025     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24026     
24027 };
24028
24029 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24030     
24031     boxLabel : '',
24032     
24033     value : '',
24034     
24035     getAutoCreate : function()
24036     {
24037         var cfg = {
24038             tag : 'div',
24039             cls : 'form-group radio',
24040             cn : [
24041                 {
24042                     tag : 'label',
24043                     cls : 'box-label',
24044                     html : this.boxLabel
24045                 }
24046             ]
24047         };
24048         
24049         return cfg;
24050     },
24051     
24052     initEvents : function() 
24053     {
24054         this.parent().register(this);
24055         
24056         this.el.on('click', this.onClick, this);
24057         
24058     },
24059     
24060     onClick : function(e)
24061     {
24062         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24063             this.setChecked(true);
24064         }
24065     },
24066     
24067     setChecked : function(state, suppressEvent)
24068     {
24069         this.parent().setValue(this.value, suppressEvent);
24070         
24071     },
24072     
24073     setBoxLabel : function(v)
24074     {
24075         this.boxLabel = v;
24076         
24077         if(this.rendered){
24078             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24079         }
24080     }
24081     
24082 });
24083  
24084
24085  /*
24086  * - LGPL
24087  *
24088  * Input
24089  * 
24090  */
24091
24092 /**
24093  * @class Roo.bootstrap.SecurePass
24094  * @extends Roo.bootstrap.Input
24095  * Bootstrap SecurePass class
24096  *
24097  * 
24098  * @constructor
24099  * Create a new SecurePass
24100  * @param {Object} config The config object
24101  */
24102  
24103 Roo.bootstrap.SecurePass = function (config) {
24104     // these go here, so the translation tool can replace them..
24105     this.errors = {
24106         PwdEmpty: "Please type a password, and then retype it to confirm.",
24107         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24108         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24109         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24110         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24111         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24112         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24113         TooWeak: "Your password is Too Weak."
24114     },
24115     this.meterLabel = "Password strength:";
24116     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24117     this.meterClass = [
24118         "roo-password-meter-tooweak", 
24119         "roo-password-meter-weak", 
24120         "roo-password-meter-medium", 
24121         "roo-password-meter-strong", 
24122         "roo-password-meter-grey"
24123     ];
24124     
24125     this.errors = {};
24126     
24127     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24128 }
24129
24130 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24131     /**
24132      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24133      * {
24134      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24135      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24136      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24137      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24138      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24139      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24140      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24141      * })
24142      */
24143     // private
24144     
24145     meterWidth: 300,
24146     errorMsg :'',    
24147     errors: false,
24148     imageRoot: '/',
24149     /**
24150      * @cfg {String/Object} Label for the strength meter (defaults to
24151      * 'Password strength:')
24152      */
24153     // private
24154     meterLabel: '',
24155     /**
24156      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24157      * ['Weak', 'Medium', 'Strong'])
24158      */
24159     // private    
24160     pwdStrengths: false,    
24161     // private
24162     strength: 0,
24163     // private
24164     _lastPwd: null,
24165     // private
24166     kCapitalLetter: 0,
24167     kSmallLetter: 1,
24168     kDigit: 2,
24169     kPunctuation: 3,
24170     
24171     insecure: false,
24172     // private
24173     initEvents: function ()
24174     {
24175         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24176
24177         if (this.el.is('input[type=password]') && Roo.isSafari) {
24178             this.el.on('keydown', this.SafariOnKeyDown, this);
24179         }
24180
24181         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24182     },
24183     // private
24184     onRender: function (ct, position)
24185     {
24186         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24187         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24188         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24189
24190         this.trigger.createChild({
24191                    cn: [
24192                     {
24193                     //id: 'PwdMeter',
24194                     tag: 'div',
24195                     cls: 'roo-password-meter-grey col-xs-12',
24196                     style: {
24197                         //width: 0,
24198                         //width: this.meterWidth + 'px'                                                
24199                         }
24200                     },
24201                     {                            
24202                          cls: 'roo-password-meter-text'                          
24203                     }
24204                 ]            
24205         });
24206
24207          
24208         if (this.hideTrigger) {
24209             this.trigger.setDisplayed(false);
24210         }
24211         this.setSize(this.width || '', this.height || '');
24212     },
24213     // private
24214     onDestroy: function ()
24215     {
24216         if (this.trigger) {
24217             this.trigger.removeAllListeners();
24218             this.trigger.remove();
24219         }
24220         if (this.wrap) {
24221             this.wrap.remove();
24222         }
24223         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24224     },
24225     // private
24226     checkStrength: function ()
24227     {
24228         var pwd = this.inputEl().getValue();
24229         if (pwd == this._lastPwd) {
24230             return;
24231         }
24232
24233         var strength;
24234         if (this.ClientSideStrongPassword(pwd)) {
24235             strength = 3;
24236         } else if (this.ClientSideMediumPassword(pwd)) {
24237             strength = 2;
24238         } else if (this.ClientSideWeakPassword(pwd)) {
24239             strength = 1;
24240         } else {
24241             strength = 0;
24242         }
24243         
24244         Roo.log('strength1: ' + strength);
24245         
24246         //var pm = this.trigger.child('div/div/div').dom;
24247         var pm = this.trigger.child('div/div');
24248         pm.removeClass(this.meterClass);
24249         pm.addClass(this.meterClass[strength]);
24250                 
24251         
24252         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24253                 
24254         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24255         
24256         this._lastPwd = pwd;
24257     },
24258     reset: function ()
24259     {
24260         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24261         
24262         this._lastPwd = '';
24263         
24264         var pm = this.trigger.child('div/div');
24265         pm.removeClass(this.meterClass);
24266         pm.addClass('roo-password-meter-grey');        
24267         
24268         
24269         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24270         
24271         pt.innerHTML = '';
24272         this.inputEl().dom.type='password';
24273     },
24274     // private
24275     validateValue: function (value)
24276     {
24277         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24278             return false;
24279         }
24280         if (value.length == 0) {
24281             if (this.allowBlank) {
24282                 this.clearInvalid();
24283                 return true;
24284             }
24285
24286             this.markInvalid(this.errors.PwdEmpty);
24287             this.errorMsg = this.errors.PwdEmpty;
24288             return false;
24289         }
24290         
24291         if(this.insecure){
24292             return true;
24293         }
24294         
24295         if (!value.match(/[\x21-\x7e]+/)) {
24296             this.markInvalid(this.errors.PwdBadChar);
24297             this.errorMsg = this.errors.PwdBadChar;
24298             return false;
24299         }
24300         if (value.length < 6) {
24301             this.markInvalid(this.errors.PwdShort);
24302             this.errorMsg = this.errors.PwdShort;
24303             return false;
24304         }
24305         if (value.length > 16) {
24306             this.markInvalid(this.errors.PwdLong);
24307             this.errorMsg = this.errors.PwdLong;
24308             return false;
24309         }
24310         var strength;
24311         if (this.ClientSideStrongPassword(value)) {
24312             strength = 3;
24313         } else if (this.ClientSideMediumPassword(value)) {
24314             strength = 2;
24315         } else if (this.ClientSideWeakPassword(value)) {
24316             strength = 1;
24317         } else {
24318             strength = 0;
24319         }
24320
24321         
24322         if (strength < 2) {
24323             //this.markInvalid(this.errors.TooWeak);
24324             this.errorMsg = this.errors.TooWeak;
24325             //return false;
24326         }
24327         
24328         
24329         console.log('strength2: ' + strength);
24330         
24331         //var pm = this.trigger.child('div/div/div').dom;
24332         
24333         var pm = this.trigger.child('div/div');
24334         pm.removeClass(this.meterClass);
24335         pm.addClass(this.meterClass[strength]);
24336                 
24337         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24338                 
24339         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24340         
24341         this.errorMsg = ''; 
24342         return true;
24343     },
24344     // private
24345     CharacterSetChecks: function (type)
24346     {
24347         this.type = type;
24348         this.fResult = false;
24349     },
24350     // private
24351     isctype: function (character, type)
24352     {
24353         switch (type) {  
24354             case this.kCapitalLetter:
24355                 if (character >= 'A' && character <= 'Z') {
24356                     return true;
24357                 }
24358                 break;
24359             
24360             case this.kSmallLetter:
24361                 if (character >= 'a' && character <= 'z') {
24362                     return true;
24363                 }
24364                 break;
24365             
24366             case this.kDigit:
24367                 if (character >= '0' && character <= '9') {
24368                     return true;
24369                 }
24370                 break;
24371             
24372             case this.kPunctuation:
24373                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24374                     return true;
24375                 }
24376                 break;
24377             
24378             default:
24379                 return false;
24380         }
24381
24382     },
24383     // private
24384     IsLongEnough: function (pwd, size)
24385     {
24386         return !(pwd == null || isNaN(size) || pwd.length < size);
24387     },
24388     // private
24389     SpansEnoughCharacterSets: function (word, nb)
24390     {
24391         if (!this.IsLongEnough(word, nb))
24392         {
24393             return false;
24394         }
24395
24396         var characterSetChecks = new Array(
24397             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24398             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24399         );
24400         
24401         for (var index = 0; index < word.length; ++index) {
24402             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24403                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24404                     characterSetChecks[nCharSet].fResult = true;
24405                     break;
24406                 }
24407             }
24408         }
24409
24410         var nCharSets = 0;
24411         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24412             if (characterSetChecks[nCharSet].fResult) {
24413                 ++nCharSets;
24414             }
24415         }
24416
24417         if (nCharSets < nb) {
24418             return false;
24419         }
24420         return true;
24421     },
24422     // private
24423     ClientSideStrongPassword: function (pwd)
24424     {
24425         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24426     },
24427     // private
24428     ClientSideMediumPassword: function (pwd)
24429     {
24430         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24431     },
24432     // private
24433     ClientSideWeakPassword: function (pwd)
24434     {
24435         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24436     }
24437           
24438 })//<script type="text/javascript">
24439
24440 /*
24441  * Based  Ext JS Library 1.1.1
24442  * Copyright(c) 2006-2007, Ext JS, LLC.
24443  * LGPL
24444  *
24445  */
24446  
24447 /**
24448  * @class Roo.HtmlEditorCore
24449  * @extends Roo.Component
24450  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24451  *
24452  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24453  */
24454
24455 Roo.HtmlEditorCore = function(config){
24456     
24457     
24458     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24459     
24460     
24461     this.addEvents({
24462         /**
24463          * @event initialize
24464          * Fires when the editor is fully initialized (including the iframe)
24465          * @param {Roo.HtmlEditorCore} this
24466          */
24467         initialize: true,
24468         /**
24469          * @event activate
24470          * Fires when the editor is first receives the focus. Any insertion must wait
24471          * until after this event.
24472          * @param {Roo.HtmlEditorCore} this
24473          */
24474         activate: true,
24475          /**
24476          * @event beforesync
24477          * Fires before the textarea is updated with content from the editor iframe. Return false
24478          * to cancel the sync.
24479          * @param {Roo.HtmlEditorCore} this
24480          * @param {String} html
24481          */
24482         beforesync: true,
24483          /**
24484          * @event beforepush
24485          * Fires before the iframe editor is updated with content from the textarea. Return false
24486          * to cancel the push.
24487          * @param {Roo.HtmlEditorCore} this
24488          * @param {String} html
24489          */
24490         beforepush: true,
24491          /**
24492          * @event sync
24493          * Fires when the textarea is updated with content from the editor iframe.
24494          * @param {Roo.HtmlEditorCore} this
24495          * @param {String} html
24496          */
24497         sync: true,
24498          /**
24499          * @event push
24500          * Fires when the iframe editor is updated with content from the textarea.
24501          * @param {Roo.HtmlEditorCore} this
24502          * @param {String} html
24503          */
24504         push: true,
24505         
24506         /**
24507          * @event editorevent
24508          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24509          * @param {Roo.HtmlEditorCore} this
24510          */
24511         editorevent: true
24512         
24513     });
24514     
24515     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24516     
24517     // defaults : white / black...
24518     this.applyBlacklists();
24519     
24520     
24521     
24522 };
24523
24524
24525 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24526
24527
24528      /**
24529      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24530      */
24531     
24532     owner : false,
24533     
24534      /**
24535      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24536      *                        Roo.resizable.
24537      */
24538     resizable : false,
24539      /**
24540      * @cfg {Number} height (in pixels)
24541      */   
24542     height: 300,
24543    /**
24544      * @cfg {Number} width (in pixels)
24545      */   
24546     width: 500,
24547     
24548     /**
24549      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24550      * 
24551      */
24552     stylesheets: false,
24553     
24554     // id of frame..
24555     frameId: false,
24556     
24557     // private properties
24558     validationEvent : false,
24559     deferHeight: true,
24560     initialized : false,
24561     activated : false,
24562     sourceEditMode : false,
24563     onFocus : Roo.emptyFn,
24564     iframePad:3,
24565     hideMode:'offsets',
24566     
24567     clearUp: true,
24568     
24569     // blacklist + whitelisted elements..
24570     black: false,
24571     white: false,
24572      
24573     bodyCls : '',
24574
24575     /**
24576      * Protected method that will not generally be called directly. It
24577      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24578      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24579      */
24580     getDocMarkup : function(){
24581         // body styles..
24582         var st = '';
24583         
24584         // inherit styels from page...?? 
24585         if (this.stylesheets === false) {
24586             
24587             Roo.get(document.head).select('style').each(function(node) {
24588                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24589             });
24590             
24591             Roo.get(document.head).select('link').each(function(node) { 
24592                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24593             });
24594             
24595         } else if (!this.stylesheets.length) {
24596                 // simple..
24597                 st = '<style type="text/css">' +
24598                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24599                    '</style>';
24600         } else {
24601             for (var i in this.stylesheets) { 
24602                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24603             }
24604             
24605         }
24606         
24607         st +=  '<style type="text/css">' +
24608             'IMG { cursor: pointer } ' +
24609         '</style>';
24610
24611         var cls = 'roo-htmleditor-body';
24612         
24613         if(this.bodyCls.length){
24614             cls += ' ' + this.bodyCls;
24615         }
24616         
24617         return '<html><head>' + st  +
24618             //<style type="text/css">' +
24619             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24620             //'</style>' +
24621             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24622     },
24623
24624     // private
24625     onRender : function(ct, position)
24626     {
24627         var _t = this;
24628         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24629         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24630         
24631         
24632         this.el.dom.style.border = '0 none';
24633         this.el.dom.setAttribute('tabIndex', -1);
24634         this.el.addClass('x-hidden hide');
24635         
24636         
24637         
24638         if(Roo.isIE){ // fix IE 1px bogus margin
24639             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24640         }
24641        
24642         
24643         this.frameId = Roo.id();
24644         
24645          
24646         
24647         var iframe = this.owner.wrap.createChild({
24648             tag: 'iframe',
24649             cls: 'form-control', // bootstrap..
24650             id: this.frameId,
24651             name: this.frameId,
24652             frameBorder : 'no',
24653             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24654         }, this.el
24655         );
24656         
24657         
24658         this.iframe = iframe.dom;
24659
24660          this.assignDocWin();
24661         
24662         this.doc.designMode = 'on';
24663        
24664         this.doc.open();
24665         this.doc.write(this.getDocMarkup());
24666         this.doc.close();
24667
24668         
24669         var task = { // must defer to wait for browser to be ready
24670             run : function(){
24671                 //console.log("run task?" + this.doc.readyState);
24672                 this.assignDocWin();
24673                 if(this.doc.body || this.doc.readyState == 'complete'){
24674                     try {
24675                         this.doc.designMode="on";
24676                     } catch (e) {
24677                         return;
24678                     }
24679                     Roo.TaskMgr.stop(task);
24680                     this.initEditor.defer(10, this);
24681                 }
24682             },
24683             interval : 10,
24684             duration: 10000,
24685             scope: this
24686         };
24687         Roo.TaskMgr.start(task);
24688
24689     },
24690
24691     // private
24692     onResize : function(w, h)
24693     {
24694          Roo.log('resize: ' +w + ',' + h );
24695         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24696         if(!this.iframe){
24697             return;
24698         }
24699         if(typeof w == 'number'){
24700             
24701             this.iframe.style.width = w + 'px';
24702         }
24703         if(typeof h == 'number'){
24704             
24705             this.iframe.style.height = h + 'px';
24706             if(this.doc){
24707                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24708             }
24709         }
24710         
24711     },
24712
24713     /**
24714      * Toggles the editor between standard and source edit mode.
24715      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24716      */
24717     toggleSourceEdit : function(sourceEditMode){
24718         
24719         this.sourceEditMode = sourceEditMode === true;
24720         
24721         if(this.sourceEditMode){
24722  
24723             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24724             
24725         }else{
24726             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24727             //this.iframe.className = '';
24728             this.deferFocus();
24729         }
24730         //this.setSize(this.owner.wrap.getSize());
24731         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24732     },
24733
24734     
24735   
24736
24737     /**
24738      * Protected method that will not generally be called directly. If you need/want
24739      * custom HTML cleanup, this is the method you should override.
24740      * @param {String} html The HTML to be cleaned
24741      * return {String} The cleaned HTML
24742      */
24743     cleanHtml : function(html){
24744         html = String(html);
24745         if(html.length > 5){
24746             if(Roo.isSafari){ // strip safari nonsense
24747                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24748             }
24749         }
24750         if(html == '&nbsp;'){
24751             html = '';
24752         }
24753         return html;
24754     },
24755
24756     /**
24757      * HTML Editor -> Textarea
24758      * Protected method that will not generally be called directly. Syncs the contents
24759      * of the editor iframe with the textarea.
24760      */
24761     syncValue : function(){
24762         if(this.initialized){
24763             var bd = (this.doc.body || this.doc.documentElement);
24764             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24765             var html = bd.innerHTML;
24766             if(Roo.isSafari){
24767                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24768                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24769                 if(m && m[1]){
24770                     html = '<div style="'+m[0]+'">' + html + '</div>';
24771                 }
24772             }
24773             html = this.cleanHtml(html);
24774             // fix up the special chars.. normaly like back quotes in word...
24775             // however we do not want to do this with chinese..
24776             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24777                 
24778                 var cc = match.charCodeAt();
24779
24780                 // Get the character value, handling surrogate pairs
24781                 if (match.length == 2) {
24782                     // It's a surrogate pair, calculate the Unicode code point
24783                     var high = match.charCodeAt(0) - 0xD800;
24784                     var low  = match.charCodeAt(1) - 0xDC00;
24785                     cc = (high * 0x400) + low + 0x10000;
24786                 }  else if (
24787                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24788                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24789                     (cc >= 0xf900 && cc < 0xfb00 )
24790                 ) {
24791                         return match;
24792                 }  
24793          
24794                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24795                 return "&#" + cc + ";";
24796                 
24797                 
24798             });
24799             
24800             
24801              
24802             if(this.owner.fireEvent('beforesync', this, html) !== false){
24803                 this.el.dom.value = html;
24804                 this.owner.fireEvent('sync', this, html);
24805             }
24806         }
24807     },
24808
24809     /**
24810      * Protected method that will not generally be called directly. Pushes the value of the textarea
24811      * into the iframe editor.
24812      */
24813     pushValue : function(){
24814         if(this.initialized){
24815             var v = this.el.dom.value.trim();
24816             
24817 //            if(v.length < 1){
24818 //                v = '&#160;';
24819 //            }
24820             
24821             if(this.owner.fireEvent('beforepush', this, v) !== false){
24822                 var d = (this.doc.body || this.doc.documentElement);
24823                 d.innerHTML = v;
24824                 this.cleanUpPaste();
24825                 this.el.dom.value = d.innerHTML;
24826                 this.owner.fireEvent('push', this, v);
24827             }
24828         }
24829     },
24830
24831     // private
24832     deferFocus : function(){
24833         this.focus.defer(10, this);
24834     },
24835
24836     // doc'ed in Field
24837     focus : function(){
24838         if(this.win && !this.sourceEditMode){
24839             this.win.focus();
24840         }else{
24841             this.el.focus();
24842         }
24843     },
24844     
24845     assignDocWin: function()
24846     {
24847         var iframe = this.iframe;
24848         
24849          if(Roo.isIE){
24850             this.doc = iframe.contentWindow.document;
24851             this.win = iframe.contentWindow;
24852         } else {
24853 //            if (!Roo.get(this.frameId)) {
24854 //                return;
24855 //            }
24856 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24857 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24858             
24859             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24860                 return;
24861             }
24862             
24863             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24864             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24865         }
24866     },
24867     
24868     // private
24869     initEditor : function(){
24870         //console.log("INIT EDITOR");
24871         this.assignDocWin();
24872         
24873         
24874         
24875         this.doc.designMode="on";
24876         this.doc.open();
24877         this.doc.write(this.getDocMarkup());
24878         this.doc.close();
24879         
24880         var dbody = (this.doc.body || this.doc.documentElement);
24881         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24882         // this copies styles from the containing element into thsi one..
24883         // not sure why we need all of this..
24884         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24885         
24886         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24887         //ss['background-attachment'] = 'fixed'; // w3c
24888         dbody.bgProperties = 'fixed'; // ie
24889         //Roo.DomHelper.applyStyles(dbody, ss);
24890         Roo.EventManager.on(this.doc, {
24891             //'mousedown': this.onEditorEvent,
24892             'mouseup': this.onEditorEvent,
24893             'dblclick': this.onEditorEvent,
24894             'click': this.onEditorEvent,
24895             'keyup': this.onEditorEvent,
24896             buffer:100,
24897             scope: this
24898         });
24899         if(Roo.isGecko){
24900             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24901         }
24902         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24903             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24904         }
24905         this.initialized = true;
24906
24907         this.owner.fireEvent('initialize', this);
24908         this.pushValue();
24909     },
24910
24911     // private
24912     onDestroy : function(){
24913         
24914         
24915         
24916         if(this.rendered){
24917             
24918             //for (var i =0; i < this.toolbars.length;i++) {
24919             //    // fixme - ask toolbars for heights?
24920             //    this.toolbars[i].onDestroy();
24921            // }
24922             
24923             //this.wrap.dom.innerHTML = '';
24924             //this.wrap.remove();
24925         }
24926     },
24927
24928     // private
24929     onFirstFocus : function(){
24930         
24931         this.assignDocWin();
24932         
24933         
24934         this.activated = true;
24935          
24936     
24937         if(Roo.isGecko){ // prevent silly gecko errors
24938             this.win.focus();
24939             var s = this.win.getSelection();
24940             if(!s.focusNode || s.focusNode.nodeType != 3){
24941                 var r = s.getRangeAt(0);
24942                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24943                 r.collapse(true);
24944                 this.deferFocus();
24945             }
24946             try{
24947                 this.execCmd('useCSS', true);
24948                 this.execCmd('styleWithCSS', false);
24949             }catch(e){}
24950         }
24951         this.owner.fireEvent('activate', this);
24952     },
24953
24954     // private
24955     adjustFont: function(btn){
24956         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24957         //if(Roo.isSafari){ // safari
24958         //    adjust *= 2;
24959        // }
24960         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24961         if(Roo.isSafari){ // safari
24962             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24963             v =  (v < 10) ? 10 : v;
24964             v =  (v > 48) ? 48 : v;
24965             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24966             
24967         }
24968         
24969         
24970         v = Math.max(1, v+adjust);
24971         
24972         this.execCmd('FontSize', v  );
24973     },
24974
24975     onEditorEvent : function(e)
24976     {
24977         this.owner.fireEvent('editorevent', this, e);
24978       //  this.updateToolbar();
24979         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24980     },
24981
24982     insertTag : function(tg)
24983     {
24984         // could be a bit smarter... -> wrap the current selected tRoo..
24985         if (tg.toLowerCase() == 'span' ||
24986             tg.toLowerCase() == 'code' ||
24987             tg.toLowerCase() == 'sup' ||
24988             tg.toLowerCase() == 'sub' 
24989             ) {
24990             
24991             range = this.createRange(this.getSelection());
24992             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24993             wrappingNode.appendChild(range.extractContents());
24994             range.insertNode(wrappingNode);
24995
24996             return;
24997             
24998             
24999             
25000         }
25001         this.execCmd("formatblock",   tg);
25002         
25003     },
25004     
25005     insertText : function(txt)
25006     {
25007         
25008         
25009         var range = this.createRange();
25010         range.deleteContents();
25011                //alert(Sender.getAttribute('label'));
25012                
25013         range.insertNode(this.doc.createTextNode(txt));
25014     } ,
25015     
25016      
25017
25018     /**
25019      * Executes a Midas editor command on the editor document and performs necessary focus and
25020      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25021      * @param {String} cmd The Midas command
25022      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25023      */
25024     relayCmd : function(cmd, value){
25025         this.win.focus();
25026         this.execCmd(cmd, value);
25027         this.owner.fireEvent('editorevent', this);
25028         //this.updateToolbar();
25029         this.owner.deferFocus();
25030     },
25031
25032     /**
25033      * Executes a Midas editor command directly on the editor document.
25034      * For visual commands, you should use {@link #relayCmd} instead.
25035      * <b>This should only be called after the editor is initialized.</b>
25036      * @param {String} cmd The Midas command
25037      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25038      */
25039     execCmd : function(cmd, value){
25040         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25041         this.syncValue();
25042     },
25043  
25044  
25045    
25046     /**
25047      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25048      * to insert tRoo.
25049      * @param {String} text | dom node.. 
25050      */
25051     insertAtCursor : function(text)
25052     {
25053         
25054         if(!this.activated){
25055             return;
25056         }
25057         /*
25058         if(Roo.isIE){
25059             this.win.focus();
25060             var r = this.doc.selection.createRange();
25061             if(r){
25062                 r.collapse(true);
25063                 r.pasteHTML(text);
25064                 this.syncValue();
25065                 this.deferFocus();
25066             
25067             }
25068             return;
25069         }
25070         */
25071         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25072             this.win.focus();
25073             
25074             
25075             // from jquery ui (MIT licenced)
25076             var range, node;
25077             var win = this.win;
25078             
25079             if (win.getSelection && win.getSelection().getRangeAt) {
25080                 range = win.getSelection().getRangeAt(0);
25081                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25082                 range.insertNode(node);
25083             } else if (win.document.selection && win.document.selection.createRange) {
25084                 // no firefox support
25085                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25086                 win.document.selection.createRange().pasteHTML(txt);
25087             } else {
25088                 // no firefox support
25089                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25090                 this.execCmd('InsertHTML', txt);
25091             } 
25092             
25093             this.syncValue();
25094             
25095             this.deferFocus();
25096         }
25097     },
25098  // private
25099     mozKeyPress : function(e){
25100         if(e.ctrlKey){
25101             var c = e.getCharCode(), cmd;
25102           
25103             if(c > 0){
25104                 c = String.fromCharCode(c).toLowerCase();
25105                 switch(c){
25106                     case 'b':
25107                         cmd = 'bold';
25108                         break;
25109                     case 'i':
25110                         cmd = 'italic';
25111                         break;
25112                     
25113                     case 'u':
25114                         cmd = 'underline';
25115                         break;
25116                     
25117                     case 'v':
25118                         this.cleanUpPaste.defer(100, this);
25119                         return;
25120                         
25121                 }
25122                 if(cmd){
25123                     this.win.focus();
25124                     this.execCmd(cmd);
25125                     this.deferFocus();
25126                     e.preventDefault();
25127                 }
25128                 
25129             }
25130         }
25131     },
25132
25133     // private
25134     fixKeys : function(){ // load time branching for fastest keydown performance
25135         if(Roo.isIE){
25136             return function(e){
25137                 var k = e.getKey(), r;
25138                 if(k == e.TAB){
25139                     e.stopEvent();
25140                     r = this.doc.selection.createRange();
25141                     if(r){
25142                         r.collapse(true);
25143                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25144                         this.deferFocus();
25145                     }
25146                     return;
25147                 }
25148                 
25149                 if(k == e.ENTER){
25150                     r = this.doc.selection.createRange();
25151                     if(r){
25152                         var target = r.parentElement();
25153                         if(!target || target.tagName.toLowerCase() != 'li'){
25154                             e.stopEvent();
25155                             r.pasteHTML('<br />');
25156                             r.collapse(false);
25157                             r.select();
25158                         }
25159                     }
25160                 }
25161                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25162                     this.cleanUpPaste.defer(100, this);
25163                     return;
25164                 }
25165                 
25166                 
25167             };
25168         }else if(Roo.isOpera){
25169             return function(e){
25170                 var k = e.getKey();
25171                 if(k == e.TAB){
25172                     e.stopEvent();
25173                     this.win.focus();
25174                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25175                     this.deferFocus();
25176                 }
25177                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25178                     this.cleanUpPaste.defer(100, this);
25179                     return;
25180                 }
25181                 
25182             };
25183         }else if(Roo.isSafari){
25184             return function(e){
25185                 var k = e.getKey();
25186                 
25187                 if(k == e.TAB){
25188                     e.stopEvent();
25189                     this.execCmd('InsertText','\t');
25190                     this.deferFocus();
25191                     return;
25192                 }
25193                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25194                     this.cleanUpPaste.defer(100, this);
25195                     return;
25196                 }
25197                 
25198              };
25199         }
25200     }(),
25201     
25202     getAllAncestors: function()
25203     {
25204         var p = this.getSelectedNode();
25205         var a = [];
25206         if (!p) {
25207             a.push(p); // push blank onto stack..
25208             p = this.getParentElement();
25209         }
25210         
25211         
25212         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25213             a.push(p);
25214             p = p.parentNode;
25215         }
25216         a.push(this.doc.body);
25217         return a;
25218     },
25219     lastSel : false,
25220     lastSelNode : false,
25221     
25222     
25223     getSelection : function() 
25224     {
25225         this.assignDocWin();
25226         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25227     },
25228     
25229     getSelectedNode: function() 
25230     {
25231         // this may only work on Gecko!!!
25232         
25233         // should we cache this!!!!
25234         
25235         
25236         
25237          
25238         var range = this.createRange(this.getSelection()).cloneRange();
25239         
25240         if (Roo.isIE) {
25241             var parent = range.parentElement();
25242             while (true) {
25243                 var testRange = range.duplicate();
25244                 testRange.moveToElementText(parent);
25245                 if (testRange.inRange(range)) {
25246                     break;
25247                 }
25248                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25249                     break;
25250                 }
25251                 parent = parent.parentElement;
25252             }
25253             return parent;
25254         }
25255         
25256         // is ancestor a text element.
25257         var ac =  range.commonAncestorContainer;
25258         if (ac.nodeType == 3) {
25259             ac = ac.parentNode;
25260         }
25261         
25262         var ar = ac.childNodes;
25263          
25264         var nodes = [];
25265         var other_nodes = [];
25266         var has_other_nodes = false;
25267         for (var i=0;i<ar.length;i++) {
25268             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25269                 continue;
25270             }
25271             // fullly contained node.
25272             
25273             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25274                 nodes.push(ar[i]);
25275                 continue;
25276             }
25277             
25278             // probably selected..
25279             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25280                 other_nodes.push(ar[i]);
25281                 continue;
25282             }
25283             // outer..
25284             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25285                 continue;
25286             }
25287             
25288             
25289             has_other_nodes = true;
25290         }
25291         if (!nodes.length && other_nodes.length) {
25292             nodes= other_nodes;
25293         }
25294         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25295             return false;
25296         }
25297         
25298         return nodes[0];
25299     },
25300     createRange: function(sel)
25301     {
25302         // this has strange effects when using with 
25303         // top toolbar - not sure if it's a great idea.
25304         //this.editor.contentWindow.focus();
25305         if (typeof sel != "undefined") {
25306             try {
25307                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25308             } catch(e) {
25309                 return this.doc.createRange();
25310             }
25311         } else {
25312             return this.doc.createRange();
25313         }
25314     },
25315     getParentElement: function()
25316     {
25317         
25318         this.assignDocWin();
25319         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25320         
25321         var range = this.createRange(sel);
25322          
25323         try {
25324             var p = range.commonAncestorContainer;
25325             while (p.nodeType == 3) { // text node
25326                 p = p.parentNode;
25327             }
25328             return p;
25329         } catch (e) {
25330             return null;
25331         }
25332     
25333     },
25334     /***
25335      *
25336      * Range intersection.. the hard stuff...
25337      *  '-1' = before
25338      *  '0' = hits..
25339      *  '1' = after.
25340      *         [ -- selected range --- ]
25341      *   [fail]                        [fail]
25342      *
25343      *    basically..
25344      *      if end is before start or  hits it. fail.
25345      *      if start is after end or hits it fail.
25346      *
25347      *   if either hits (but other is outside. - then it's not 
25348      *   
25349      *    
25350      **/
25351     
25352     
25353     // @see http://www.thismuchiknow.co.uk/?p=64.
25354     rangeIntersectsNode : function(range, node)
25355     {
25356         var nodeRange = node.ownerDocument.createRange();
25357         try {
25358             nodeRange.selectNode(node);
25359         } catch (e) {
25360             nodeRange.selectNodeContents(node);
25361         }
25362     
25363         var rangeStartRange = range.cloneRange();
25364         rangeStartRange.collapse(true);
25365     
25366         var rangeEndRange = range.cloneRange();
25367         rangeEndRange.collapse(false);
25368     
25369         var nodeStartRange = nodeRange.cloneRange();
25370         nodeStartRange.collapse(true);
25371     
25372         var nodeEndRange = nodeRange.cloneRange();
25373         nodeEndRange.collapse(false);
25374     
25375         return rangeStartRange.compareBoundaryPoints(
25376                  Range.START_TO_START, nodeEndRange) == -1 &&
25377                rangeEndRange.compareBoundaryPoints(
25378                  Range.START_TO_START, nodeStartRange) == 1;
25379         
25380          
25381     },
25382     rangeCompareNode : function(range, node)
25383     {
25384         var nodeRange = node.ownerDocument.createRange();
25385         try {
25386             nodeRange.selectNode(node);
25387         } catch (e) {
25388             nodeRange.selectNodeContents(node);
25389         }
25390         
25391         
25392         range.collapse(true);
25393     
25394         nodeRange.collapse(true);
25395      
25396         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25397         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25398          
25399         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25400         
25401         var nodeIsBefore   =  ss == 1;
25402         var nodeIsAfter    = ee == -1;
25403         
25404         if (nodeIsBefore && nodeIsAfter) {
25405             return 0; // outer
25406         }
25407         if (!nodeIsBefore && nodeIsAfter) {
25408             return 1; //right trailed.
25409         }
25410         
25411         if (nodeIsBefore && !nodeIsAfter) {
25412             return 2;  // left trailed.
25413         }
25414         // fully contined.
25415         return 3;
25416     },
25417
25418     // private? - in a new class?
25419     cleanUpPaste :  function()
25420     {
25421         // cleans up the whole document..
25422         Roo.log('cleanuppaste');
25423         
25424         this.cleanUpChildren(this.doc.body);
25425         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25426         if (clean != this.doc.body.innerHTML) {
25427             this.doc.body.innerHTML = clean;
25428         }
25429         
25430     },
25431     
25432     cleanWordChars : function(input) {// change the chars to hex code
25433         var he = Roo.HtmlEditorCore;
25434         
25435         var output = input;
25436         Roo.each(he.swapCodes, function(sw) { 
25437             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25438             
25439             output = output.replace(swapper, sw[1]);
25440         });
25441         
25442         return output;
25443     },
25444     
25445     
25446     cleanUpChildren : function (n)
25447     {
25448         if (!n.childNodes.length) {
25449             return;
25450         }
25451         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25452            this.cleanUpChild(n.childNodes[i]);
25453         }
25454     },
25455     
25456     
25457         
25458     
25459     cleanUpChild : function (node)
25460     {
25461         var ed = this;
25462         //console.log(node);
25463         if (node.nodeName == "#text") {
25464             // clean up silly Windows -- stuff?
25465             return; 
25466         }
25467         if (node.nodeName == "#comment") {
25468             node.parentNode.removeChild(node);
25469             // clean up silly Windows -- stuff?
25470             return; 
25471         }
25472         var lcname = node.tagName.toLowerCase();
25473         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25474         // whitelist of tags..
25475         
25476         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25477             // remove node.
25478             node.parentNode.removeChild(node);
25479             return;
25480             
25481         }
25482         
25483         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25484         
25485         // spans with no attributes - just remove them..
25486         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25487             remove_keep_children = true;
25488         }
25489         
25490         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25491         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25492         
25493         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25494         //    remove_keep_children = true;
25495         //}
25496         
25497         if (remove_keep_children) {
25498             this.cleanUpChildren(node);
25499             // inserts everything just before this node...
25500             while (node.childNodes.length) {
25501                 var cn = node.childNodes[0];
25502                 node.removeChild(cn);
25503                 node.parentNode.insertBefore(cn, node);
25504             }
25505             node.parentNode.removeChild(node);
25506             return;
25507         }
25508         
25509         if (!node.attributes || !node.attributes.length) {
25510             
25511           
25512             
25513             
25514             this.cleanUpChildren(node);
25515             return;
25516         }
25517         
25518         function cleanAttr(n,v)
25519         {
25520             
25521             if (v.match(/^\./) || v.match(/^\//)) {
25522                 return;
25523             }
25524             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25525                 return;
25526             }
25527             if (v.match(/^#/)) {
25528                 return;
25529             }
25530             if (v.match(/^\{/)) { // allow template editing.
25531                 return;
25532             }
25533 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25534             node.removeAttribute(n);
25535             
25536         }
25537         
25538         var cwhite = this.cwhite;
25539         var cblack = this.cblack;
25540             
25541         function cleanStyle(n,v)
25542         {
25543             if (v.match(/expression/)) { //XSS?? should we even bother..
25544                 node.removeAttribute(n);
25545                 return;
25546             }
25547             
25548             var parts = v.split(/;/);
25549             var clean = [];
25550             
25551             Roo.each(parts, function(p) {
25552                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25553                 if (!p.length) {
25554                     return true;
25555                 }
25556                 var l = p.split(':').shift().replace(/\s+/g,'');
25557                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25558                 
25559                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25560 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25561                     //node.removeAttribute(n);
25562                     return true;
25563                 }
25564                 //Roo.log()
25565                 // only allow 'c whitelisted system attributes'
25566                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25567 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25568                     //node.removeAttribute(n);
25569                     return true;
25570                 }
25571                 
25572                 
25573                  
25574                 
25575                 clean.push(p);
25576                 return true;
25577             });
25578             if (clean.length) { 
25579                 node.setAttribute(n, clean.join(';'));
25580             } else {
25581                 node.removeAttribute(n);
25582             }
25583             
25584         }
25585         
25586         
25587         for (var i = node.attributes.length-1; i > -1 ; i--) {
25588             var a = node.attributes[i];
25589             //console.log(a);
25590             
25591             if (a.name.toLowerCase().substr(0,2)=='on')  {
25592                 node.removeAttribute(a.name);
25593                 continue;
25594             }
25595             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25596                 node.removeAttribute(a.name);
25597                 continue;
25598             }
25599             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25600                 cleanAttr(a.name,a.value); // fixme..
25601                 continue;
25602             }
25603             if (a.name == 'style') {
25604                 cleanStyle(a.name,a.value);
25605                 continue;
25606             }
25607             /// clean up MS crap..
25608             // tecnically this should be a list of valid class'es..
25609             
25610             
25611             if (a.name == 'class') {
25612                 if (a.value.match(/^Mso/)) {
25613                     node.removeAttribute('class');
25614                 }
25615                 
25616                 if (a.value.match(/^body$/)) {
25617                     node.removeAttribute('class');
25618                 }
25619                 continue;
25620             }
25621             
25622             // style cleanup!?
25623             // class cleanup?
25624             
25625         }
25626         
25627         
25628         this.cleanUpChildren(node);
25629         
25630         
25631     },
25632     
25633     /**
25634      * Clean up MS wordisms...
25635      */
25636     cleanWord : function(node)
25637     {
25638         if (!node) {
25639             this.cleanWord(this.doc.body);
25640             return;
25641         }
25642         
25643         if(
25644                 node.nodeName == 'SPAN' &&
25645                 !node.hasAttributes() &&
25646                 node.childNodes.length == 1 &&
25647                 node.firstChild.nodeName == "#text"  
25648         ) {
25649             var textNode = node.firstChild;
25650             node.removeChild(textNode);
25651             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25652                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25653             }
25654             node.parentNode.insertBefore(textNode, node);
25655             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25656                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25657             }
25658             node.parentNode.removeChild(node);
25659         }
25660         
25661         if (node.nodeName == "#text") {
25662             // clean up silly Windows -- stuff?
25663             return; 
25664         }
25665         if (node.nodeName == "#comment") {
25666             node.parentNode.removeChild(node);
25667             // clean up silly Windows -- stuff?
25668             return; 
25669         }
25670         
25671         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25672             node.parentNode.removeChild(node);
25673             return;
25674         }
25675         //Roo.log(node.tagName);
25676         // remove - but keep children..
25677         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25678             //Roo.log('-- removed');
25679             while (node.childNodes.length) {
25680                 var cn = node.childNodes[0];
25681                 node.removeChild(cn);
25682                 node.parentNode.insertBefore(cn, node);
25683                 // move node to parent - and clean it..
25684                 this.cleanWord(cn);
25685             }
25686             node.parentNode.removeChild(node);
25687             /// no need to iterate chidlren = it's got none..
25688             //this.iterateChildren(node, this.cleanWord);
25689             return;
25690         }
25691         // clean styles
25692         if (node.className.length) {
25693             
25694             var cn = node.className.split(/\W+/);
25695             var cna = [];
25696             Roo.each(cn, function(cls) {
25697                 if (cls.match(/Mso[a-zA-Z]+/)) {
25698                     return;
25699                 }
25700                 cna.push(cls);
25701             });
25702             node.className = cna.length ? cna.join(' ') : '';
25703             if (!cna.length) {
25704                 node.removeAttribute("class");
25705             }
25706         }
25707         
25708         if (node.hasAttribute("lang")) {
25709             node.removeAttribute("lang");
25710         }
25711         
25712         if (node.hasAttribute("style")) {
25713             
25714             var styles = node.getAttribute("style").split(";");
25715             var nstyle = [];
25716             Roo.each(styles, function(s) {
25717                 if (!s.match(/:/)) {
25718                     return;
25719                 }
25720                 var kv = s.split(":");
25721                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25722                     return;
25723                 }
25724                 // what ever is left... we allow.
25725                 nstyle.push(s);
25726             });
25727             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25728             if (!nstyle.length) {
25729                 node.removeAttribute('style');
25730             }
25731         }
25732         this.iterateChildren(node, this.cleanWord);
25733         
25734         
25735         
25736     },
25737     /**
25738      * iterateChildren of a Node, calling fn each time, using this as the scole..
25739      * @param {DomNode} node node to iterate children of.
25740      * @param {Function} fn method of this class to call on each item.
25741      */
25742     iterateChildren : function(node, fn)
25743     {
25744         if (!node.childNodes.length) {
25745                 return;
25746         }
25747         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25748            fn.call(this, node.childNodes[i])
25749         }
25750     },
25751     
25752     
25753     /**
25754      * cleanTableWidths.
25755      *
25756      * Quite often pasting from word etc.. results in tables with column and widths.
25757      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25758      *
25759      */
25760     cleanTableWidths : function(node)
25761     {
25762          
25763          
25764         if (!node) {
25765             this.cleanTableWidths(this.doc.body);
25766             return;
25767         }
25768         
25769         // ignore list...
25770         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25771             return; 
25772         }
25773         Roo.log(node.tagName);
25774         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25775             this.iterateChildren(node, this.cleanTableWidths);
25776             return;
25777         }
25778         if (node.hasAttribute('width')) {
25779             node.removeAttribute('width');
25780         }
25781         
25782          
25783         if (node.hasAttribute("style")) {
25784             // pretty basic...
25785             
25786             var styles = node.getAttribute("style").split(";");
25787             var nstyle = [];
25788             Roo.each(styles, function(s) {
25789                 if (!s.match(/:/)) {
25790                     return;
25791                 }
25792                 var kv = s.split(":");
25793                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25794                     return;
25795                 }
25796                 // what ever is left... we allow.
25797                 nstyle.push(s);
25798             });
25799             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25800             if (!nstyle.length) {
25801                 node.removeAttribute('style');
25802             }
25803         }
25804         
25805         this.iterateChildren(node, this.cleanTableWidths);
25806         
25807         
25808     },
25809     
25810     
25811     
25812     
25813     domToHTML : function(currentElement, depth, nopadtext) {
25814         
25815         depth = depth || 0;
25816         nopadtext = nopadtext || false;
25817     
25818         if (!currentElement) {
25819             return this.domToHTML(this.doc.body);
25820         }
25821         
25822         //Roo.log(currentElement);
25823         var j;
25824         var allText = false;
25825         var nodeName = currentElement.nodeName;
25826         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25827         
25828         if  (nodeName == '#text') {
25829             
25830             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25831         }
25832         
25833         
25834         var ret = '';
25835         if (nodeName != 'BODY') {
25836              
25837             var i = 0;
25838             // Prints the node tagName, such as <A>, <IMG>, etc
25839             if (tagName) {
25840                 var attr = [];
25841                 for(i = 0; i < currentElement.attributes.length;i++) {
25842                     // quoting?
25843                     var aname = currentElement.attributes.item(i).name;
25844                     if (!currentElement.attributes.item(i).value.length) {
25845                         continue;
25846                     }
25847                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25848                 }
25849                 
25850                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25851             } 
25852             else {
25853                 
25854                 // eack
25855             }
25856         } else {
25857             tagName = false;
25858         }
25859         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25860             return ret;
25861         }
25862         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25863             nopadtext = true;
25864         }
25865         
25866         
25867         // Traverse the tree
25868         i = 0;
25869         var currentElementChild = currentElement.childNodes.item(i);
25870         var allText = true;
25871         var innerHTML  = '';
25872         lastnode = '';
25873         while (currentElementChild) {
25874             // Formatting code (indent the tree so it looks nice on the screen)
25875             var nopad = nopadtext;
25876             if (lastnode == 'SPAN') {
25877                 nopad  = true;
25878             }
25879             // text
25880             if  (currentElementChild.nodeName == '#text') {
25881                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25882                 toadd = nopadtext ? toadd : toadd.trim();
25883                 if (!nopad && toadd.length > 80) {
25884                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25885                 }
25886                 innerHTML  += toadd;
25887                 
25888                 i++;
25889                 currentElementChild = currentElement.childNodes.item(i);
25890                 lastNode = '';
25891                 continue;
25892             }
25893             allText = false;
25894             
25895             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25896                 
25897             // Recursively traverse the tree structure of the child node
25898             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25899             lastnode = currentElementChild.nodeName;
25900             i++;
25901             currentElementChild=currentElement.childNodes.item(i);
25902         }
25903         
25904         ret += innerHTML;
25905         
25906         if (!allText) {
25907                 // The remaining code is mostly for formatting the tree
25908             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25909         }
25910         
25911         
25912         if (tagName) {
25913             ret+= "</"+tagName+">";
25914         }
25915         return ret;
25916         
25917     },
25918         
25919     applyBlacklists : function()
25920     {
25921         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25922         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25923         
25924         this.white = [];
25925         this.black = [];
25926         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25927             if (b.indexOf(tag) > -1) {
25928                 return;
25929             }
25930             this.white.push(tag);
25931             
25932         }, this);
25933         
25934         Roo.each(w, function(tag) {
25935             if (b.indexOf(tag) > -1) {
25936                 return;
25937             }
25938             if (this.white.indexOf(tag) > -1) {
25939                 return;
25940             }
25941             this.white.push(tag);
25942             
25943         }, this);
25944         
25945         
25946         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25947             if (w.indexOf(tag) > -1) {
25948                 return;
25949             }
25950             this.black.push(tag);
25951             
25952         }, this);
25953         
25954         Roo.each(b, function(tag) {
25955             if (w.indexOf(tag) > -1) {
25956                 return;
25957             }
25958             if (this.black.indexOf(tag) > -1) {
25959                 return;
25960             }
25961             this.black.push(tag);
25962             
25963         }, this);
25964         
25965         
25966         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25967         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25968         
25969         this.cwhite = [];
25970         this.cblack = [];
25971         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25972             if (b.indexOf(tag) > -1) {
25973                 return;
25974             }
25975             this.cwhite.push(tag);
25976             
25977         }, this);
25978         
25979         Roo.each(w, function(tag) {
25980             if (b.indexOf(tag) > -1) {
25981                 return;
25982             }
25983             if (this.cwhite.indexOf(tag) > -1) {
25984                 return;
25985             }
25986             this.cwhite.push(tag);
25987             
25988         }, this);
25989         
25990         
25991         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25992             if (w.indexOf(tag) > -1) {
25993                 return;
25994             }
25995             this.cblack.push(tag);
25996             
25997         }, this);
25998         
25999         Roo.each(b, function(tag) {
26000             if (w.indexOf(tag) > -1) {
26001                 return;
26002             }
26003             if (this.cblack.indexOf(tag) > -1) {
26004                 return;
26005             }
26006             this.cblack.push(tag);
26007             
26008         }, this);
26009     },
26010     
26011     setStylesheets : function(stylesheets)
26012     {
26013         if(typeof(stylesheets) == 'string'){
26014             Roo.get(this.iframe.contentDocument.head).createChild({
26015                 tag : 'link',
26016                 rel : 'stylesheet',
26017                 type : 'text/css',
26018                 href : stylesheets
26019             });
26020             
26021             return;
26022         }
26023         var _this = this;
26024      
26025         Roo.each(stylesheets, function(s) {
26026             if(!s.length){
26027                 return;
26028             }
26029             
26030             Roo.get(_this.iframe.contentDocument.head).createChild({
26031                 tag : 'link',
26032                 rel : 'stylesheet',
26033                 type : 'text/css',
26034                 href : s
26035             });
26036         });
26037
26038         
26039     },
26040     
26041     removeStylesheets : function()
26042     {
26043         var _this = this;
26044         
26045         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26046             s.remove();
26047         });
26048     },
26049     
26050     setStyle : function(style)
26051     {
26052         Roo.get(this.iframe.contentDocument.head).createChild({
26053             tag : 'style',
26054             type : 'text/css',
26055             html : style
26056         });
26057
26058         return;
26059     }
26060     
26061     // hide stuff that is not compatible
26062     /**
26063      * @event blur
26064      * @hide
26065      */
26066     /**
26067      * @event change
26068      * @hide
26069      */
26070     /**
26071      * @event focus
26072      * @hide
26073      */
26074     /**
26075      * @event specialkey
26076      * @hide
26077      */
26078     /**
26079      * @cfg {String} fieldClass @hide
26080      */
26081     /**
26082      * @cfg {String} focusClass @hide
26083      */
26084     /**
26085      * @cfg {String} autoCreate @hide
26086      */
26087     /**
26088      * @cfg {String} inputType @hide
26089      */
26090     /**
26091      * @cfg {String} invalidClass @hide
26092      */
26093     /**
26094      * @cfg {String} invalidText @hide
26095      */
26096     /**
26097      * @cfg {String} msgFx @hide
26098      */
26099     /**
26100      * @cfg {String} validateOnBlur @hide
26101      */
26102 });
26103
26104 Roo.HtmlEditorCore.white = [
26105         'area', 'br', 'img', 'input', 'hr', 'wbr',
26106         
26107        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26108        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26109        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26110        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26111        'table',   'ul',         'xmp', 
26112        
26113        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26114       'thead',   'tr', 
26115      
26116       'dir', 'menu', 'ol', 'ul', 'dl',
26117        
26118       'embed',  'object'
26119 ];
26120
26121
26122 Roo.HtmlEditorCore.black = [
26123     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26124         'applet', // 
26125         'base',   'basefont', 'bgsound', 'blink',  'body', 
26126         'frame',  'frameset', 'head',    'html',   'ilayer', 
26127         'iframe', 'layer',  'link',     'meta',    'object',   
26128         'script', 'style' ,'title',  'xml' // clean later..
26129 ];
26130 Roo.HtmlEditorCore.clean = [
26131     'script', 'style', 'title', 'xml'
26132 ];
26133 Roo.HtmlEditorCore.remove = [
26134     'font'
26135 ];
26136 // attributes..
26137
26138 Roo.HtmlEditorCore.ablack = [
26139     'on'
26140 ];
26141     
26142 Roo.HtmlEditorCore.aclean = [ 
26143     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26144 ];
26145
26146 // protocols..
26147 Roo.HtmlEditorCore.pwhite= [
26148         'http',  'https',  'mailto'
26149 ];
26150
26151 // white listed style attributes.
26152 Roo.HtmlEditorCore.cwhite= [
26153       //  'text-align', /// default is to allow most things..
26154       
26155          
26156 //        'font-size'//??
26157 ];
26158
26159 // black listed style attributes.
26160 Roo.HtmlEditorCore.cblack= [
26161       //  'font-size' -- this can be set by the project 
26162 ];
26163
26164
26165 Roo.HtmlEditorCore.swapCodes   =[ 
26166     [    8211, "&#8211;" ], 
26167     [    8212, "&#8212;" ], 
26168     [    8216,  "'" ],  
26169     [    8217, "'" ],  
26170     [    8220, '"' ],  
26171     [    8221, '"' ],  
26172     [    8226, "*" ],  
26173     [    8230, "..." ]
26174 ]; 
26175
26176     /*
26177  * - LGPL
26178  *
26179  * HtmlEditor
26180  * 
26181  */
26182
26183 /**
26184  * @class Roo.bootstrap.HtmlEditor
26185  * @extends Roo.bootstrap.TextArea
26186  * Bootstrap HtmlEditor class
26187
26188  * @constructor
26189  * Create a new HtmlEditor
26190  * @param {Object} config The config object
26191  */
26192
26193 Roo.bootstrap.HtmlEditor = function(config){
26194     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26195     if (!this.toolbars) {
26196         this.toolbars = [];
26197     }
26198     
26199     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26200     this.addEvents({
26201             /**
26202              * @event initialize
26203              * Fires when the editor is fully initialized (including the iframe)
26204              * @param {HtmlEditor} this
26205              */
26206             initialize: true,
26207             /**
26208              * @event activate
26209              * Fires when the editor is first receives the focus. Any insertion must wait
26210              * until after this event.
26211              * @param {HtmlEditor} this
26212              */
26213             activate: true,
26214              /**
26215              * @event beforesync
26216              * Fires before the textarea is updated with content from the editor iframe. Return false
26217              * to cancel the sync.
26218              * @param {HtmlEditor} this
26219              * @param {String} html
26220              */
26221             beforesync: true,
26222              /**
26223              * @event beforepush
26224              * Fires before the iframe editor is updated with content from the textarea. Return false
26225              * to cancel the push.
26226              * @param {HtmlEditor} this
26227              * @param {String} html
26228              */
26229             beforepush: true,
26230              /**
26231              * @event sync
26232              * Fires when the textarea is updated with content from the editor iframe.
26233              * @param {HtmlEditor} this
26234              * @param {String} html
26235              */
26236             sync: true,
26237              /**
26238              * @event push
26239              * Fires when the iframe editor is updated with content from the textarea.
26240              * @param {HtmlEditor} this
26241              * @param {String} html
26242              */
26243             push: true,
26244              /**
26245              * @event editmodechange
26246              * Fires when the editor switches edit modes
26247              * @param {HtmlEditor} this
26248              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26249              */
26250             editmodechange: true,
26251             /**
26252              * @event editorevent
26253              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26254              * @param {HtmlEditor} this
26255              */
26256             editorevent: true,
26257             /**
26258              * @event firstfocus
26259              * Fires when on first focus - needed by toolbars..
26260              * @param {HtmlEditor} this
26261              */
26262             firstfocus: true,
26263             /**
26264              * @event autosave
26265              * Auto save the htmlEditor value as a file into Events
26266              * @param {HtmlEditor} this
26267              */
26268             autosave: true,
26269             /**
26270              * @event savedpreview
26271              * preview the saved version of htmlEditor
26272              * @param {HtmlEditor} this
26273              */
26274             savedpreview: true
26275         });
26276 };
26277
26278
26279 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26280     
26281     
26282       /**
26283      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26284      */
26285     toolbars : false,
26286     
26287      /**
26288     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26289     */
26290     btns : [],
26291    
26292      /**
26293      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26294      *                        Roo.resizable.
26295      */
26296     resizable : false,
26297      /**
26298      * @cfg {Number} height (in pixels)
26299      */   
26300     height: 300,
26301    /**
26302      * @cfg {Number} width (in pixels)
26303      */   
26304     width: false,
26305     
26306     /**
26307      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26308      * 
26309      */
26310     stylesheets: false,
26311     
26312     // id of frame..
26313     frameId: false,
26314     
26315     // private properties
26316     validationEvent : false,
26317     deferHeight: true,
26318     initialized : false,
26319     activated : false,
26320     
26321     onFocus : Roo.emptyFn,
26322     iframePad:3,
26323     hideMode:'offsets',
26324     
26325     tbContainer : false,
26326     
26327     bodyCls : '',
26328     
26329     toolbarContainer :function() {
26330         return this.wrap.select('.x-html-editor-tb',true).first();
26331     },
26332
26333     /**
26334      * Protected method that will not generally be called directly. It
26335      * is called when the editor creates its toolbar. Override this method if you need to
26336      * add custom toolbar buttons.
26337      * @param {HtmlEditor} editor
26338      */
26339     createToolbar : function(){
26340         Roo.log('renewing');
26341         Roo.log("create toolbars");
26342         
26343         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26344         this.toolbars[0].render(this.toolbarContainer());
26345         
26346         return;
26347         
26348 //        if (!editor.toolbars || !editor.toolbars.length) {
26349 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26350 //        }
26351 //        
26352 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26353 //            editor.toolbars[i] = Roo.factory(
26354 //                    typeof(editor.toolbars[i]) == 'string' ?
26355 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26356 //                Roo.bootstrap.HtmlEditor);
26357 //            editor.toolbars[i].init(editor);
26358 //        }
26359     },
26360
26361      
26362     // private
26363     onRender : function(ct, position)
26364     {
26365        // Roo.log("Call onRender: " + this.xtype);
26366         var _t = this;
26367         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26368       
26369         this.wrap = this.inputEl().wrap({
26370             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26371         });
26372         
26373         this.editorcore.onRender(ct, position);
26374          
26375         if (this.resizable) {
26376             this.resizeEl = new Roo.Resizable(this.wrap, {
26377                 pinned : true,
26378                 wrap: true,
26379                 dynamic : true,
26380                 minHeight : this.height,
26381                 height: this.height,
26382                 handles : this.resizable,
26383                 width: this.width,
26384                 listeners : {
26385                     resize : function(r, w, h) {
26386                         _t.onResize(w,h); // -something
26387                     }
26388                 }
26389             });
26390             
26391         }
26392         this.createToolbar(this);
26393        
26394         
26395         if(!this.width && this.resizable){
26396             this.setSize(this.wrap.getSize());
26397         }
26398         if (this.resizeEl) {
26399             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26400             // should trigger onReize..
26401         }
26402         
26403     },
26404
26405     // private
26406     onResize : function(w, h)
26407     {
26408         Roo.log('resize: ' +w + ',' + h );
26409         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26410         var ew = false;
26411         var eh = false;
26412         
26413         if(this.inputEl() ){
26414             if(typeof w == 'number'){
26415                 var aw = w - this.wrap.getFrameWidth('lr');
26416                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26417                 ew = aw;
26418             }
26419             if(typeof h == 'number'){
26420                  var tbh = -11;  // fixme it needs to tool bar size!
26421                 for (var i =0; i < this.toolbars.length;i++) {
26422                     // fixme - ask toolbars for heights?
26423                     tbh += this.toolbars[i].el.getHeight();
26424                     //if (this.toolbars[i].footer) {
26425                     //    tbh += this.toolbars[i].footer.el.getHeight();
26426                     //}
26427                 }
26428               
26429                 
26430                 
26431                 
26432                 
26433                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26434                 ah -= 5; // knock a few pixes off for look..
26435                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26436                 var eh = ah;
26437             }
26438         }
26439         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26440         this.editorcore.onResize(ew,eh);
26441         
26442     },
26443
26444     /**
26445      * Toggles the editor between standard and source edit mode.
26446      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26447      */
26448     toggleSourceEdit : function(sourceEditMode)
26449     {
26450         this.editorcore.toggleSourceEdit(sourceEditMode);
26451         
26452         if(this.editorcore.sourceEditMode){
26453             Roo.log('editor - showing textarea');
26454             
26455 //            Roo.log('in');
26456 //            Roo.log(this.syncValue());
26457             this.syncValue();
26458             this.inputEl().removeClass(['hide', 'x-hidden']);
26459             this.inputEl().dom.removeAttribute('tabIndex');
26460             this.inputEl().focus();
26461         }else{
26462             Roo.log('editor - hiding textarea');
26463 //            Roo.log('out')
26464 //            Roo.log(this.pushValue()); 
26465             this.pushValue();
26466             
26467             this.inputEl().addClass(['hide', 'x-hidden']);
26468             this.inputEl().dom.setAttribute('tabIndex', -1);
26469             //this.deferFocus();
26470         }
26471          
26472         if(this.resizable){
26473             this.setSize(this.wrap.getSize());
26474         }
26475         
26476         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26477     },
26478  
26479     // private (for BoxComponent)
26480     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26481
26482     // private (for BoxComponent)
26483     getResizeEl : function(){
26484         return this.wrap;
26485     },
26486
26487     // private (for BoxComponent)
26488     getPositionEl : function(){
26489         return this.wrap;
26490     },
26491
26492     // private
26493     initEvents : function(){
26494         this.originalValue = this.getValue();
26495     },
26496
26497 //    /**
26498 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26499 //     * @method
26500 //     */
26501 //    markInvalid : Roo.emptyFn,
26502 //    /**
26503 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26504 //     * @method
26505 //     */
26506 //    clearInvalid : Roo.emptyFn,
26507
26508     setValue : function(v){
26509         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26510         this.editorcore.pushValue();
26511     },
26512
26513      
26514     // private
26515     deferFocus : function(){
26516         this.focus.defer(10, this);
26517     },
26518
26519     // doc'ed in Field
26520     focus : function(){
26521         this.editorcore.focus();
26522         
26523     },
26524       
26525
26526     // private
26527     onDestroy : function(){
26528         
26529         
26530         
26531         if(this.rendered){
26532             
26533             for (var i =0; i < this.toolbars.length;i++) {
26534                 // fixme - ask toolbars for heights?
26535                 this.toolbars[i].onDestroy();
26536             }
26537             
26538             this.wrap.dom.innerHTML = '';
26539             this.wrap.remove();
26540         }
26541     },
26542
26543     // private
26544     onFirstFocus : function(){
26545         //Roo.log("onFirstFocus");
26546         this.editorcore.onFirstFocus();
26547          for (var i =0; i < this.toolbars.length;i++) {
26548             this.toolbars[i].onFirstFocus();
26549         }
26550         
26551     },
26552     
26553     // private
26554     syncValue : function()
26555     {   
26556         this.editorcore.syncValue();
26557     },
26558     
26559     pushValue : function()
26560     {   
26561         this.editorcore.pushValue();
26562     }
26563      
26564     
26565     // hide stuff that is not compatible
26566     /**
26567      * @event blur
26568      * @hide
26569      */
26570     /**
26571      * @event change
26572      * @hide
26573      */
26574     /**
26575      * @event focus
26576      * @hide
26577      */
26578     /**
26579      * @event specialkey
26580      * @hide
26581      */
26582     /**
26583      * @cfg {String} fieldClass @hide
26584      */
26585     /**
26586      * @cfg {String} focusClass @hide
26587      */
26588     /**
26589      * @cfg {String} autoCreate @hide
26590      */
26591     /**
26592      * @cfg {String} inputType @hide
26593      */
26594      
26595     /**
26596      * @cfg {String} invalidText @hide
26597      */
26598     /**
26599      * @cfg {String} msgFx @hide
26600      */
26601     /**
26602      * @cfg {String} validateOnBlur @hide
26603      */
26604 });
26605  
26606     
26607    
26608    
26609    
26610       
26611 Roo.namespace('Roo.bootstrap.htmleditor');
26612 /**
26613  * @class Roo.bootstrap.HtmlEditorToolbar1
26614  * Basic Toolbar
26615  * 
26616  * @example
26617  * Usage:
26618  *
26619  new Roo.bootstrap.HtmlEditor({
26620     ....
26621     toolbars : [
26622         new Roo.bootstrap.HtmlEditorToolbar1({
26623             disable : { fonts: 1 , format: 1, ..., ... , ...],
26624             btns : [ .... ]
26625         })
26626     }
26627      
26628  * 
26629  * @cfg {Object} disable List of elements to disable..
26630  * @cfg {Array} btns List of additional buttons.
26631  * 
26632  * 
26633  * NEEDS Extra CSS? 
26634  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26635  */
26636  
26637 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26638 {
26639     
26640     Roo.apply(this, config);
26641     
26642     // default disabled, based on 'good practice'..
26643     this.disable = this.disable || {};
26644     Roo.applyIf(this.disable, {
26645         fontSize : true,
26646         colors : true,
26647         specialElements : true
26648     });
26649     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26650     
26651     this.editor = config.editor;
26652     this.editorcore = config.editor.editorcore;
26653     
26654     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26655     
26656     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26657     // dont call parent... till later.
26658 }
26659 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26660      
26661     bar : true,
26662     
26663     editor : false,
26664     editorcore : false,
26665     
26666     
26667     formats : [
26668         "p" ,  
26669         "h1","h2","h3","h4","h5","h6", 
26670         "pre", "code", 
26671         "abbr", "acronym", "address", "cite", "samp", "var",
26672         'div','span'
26673     ],
26674     
26675     onRender : function(ct, position)
26676     {
26677        // Roo.log("Call onRender: " + this.xtype);
26678         
26679        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26680        Roo.log(this.el);
26681        this.el.dom.style.marginBottom = '0';
26682        var _this = this;
26683        var editorcore = this.editorcore;
26684        var editor= this.editor;
26685        
26686        var children = [];
26687        var btn = function(id,cmd , toggle, handler, html){
26688        
26689             var  event = toggle ? 'toggle' : 'click';
26690        
26691             var a = {
26692                 size : 'sm',
26693                 xtype: 'Button',
26694                 xns: Roo.bootstrap,
26695                 //glyphicon : id,
26696                 fa: id,
26697                 cmd : id || cmd,
26698                 enableToggle:toggle !== false,
26699                 html : html || '',
26700                 pressed : toggle ? false : null,
26701                 listeners : {}
26702             };
26703             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26704                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26705             };
26706             children.push(a);
26707             return a;
26708        }
26709        
26710     //    var cb_box = function...
26711         
26712         var style = {
26713                 xtype: 'Button',
26714                 size : 'sm',
26715                 xns: Roo.bootstrap,
26716                 fa : 'font',
26717                 //html : 'submit'
26718                 menu : {
26719                     xtype: 'Menu',
26720                     xns: Roo.bootstrap,
26721                     items:  []
26722                 }
26723         };
26724         Roo.each(this.formats, function(f) {
26725             style.menu.items.push({
26726                 xtype :'MenuItem',
26727                 xns: Roo.bootstrap,
26728                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26729                 tagname : f,
26730                 listeners : {
26731                     click : function()
26732                     {
26733                         editorcore.insertTag(this.tagname);
26734                         editor.focus();
26735                     }
26736                 }
26737                 
26738             });
26739         });
26740         children.push(style);   
26741         
26742         btn('bold',false,true);
26743         btn('italic',false,true);
26744         btn('align-left', 'justifyleft',true);
26745         btn('align-center', 'justifycenter',true);
26746         btn('align-right' , 'justifyright',true);
26747         btn('link', false, false, function(btn) {
26748             //Roo.log("create link?");
26749             var url = prompt(this.createLinkText, this.defaultLinkValue);
26750             if(url && url != 'http:/'+'/'){
26751                 this.editorcore.relayCmd('createlink', url);
26752             }
26753         }),
26754         btn('list','insertunorderedlist',true);
26755         btn('pencil', false,true, function(btn){
26756                 Roo.log(this);
26757                 this.toggleSourceEdit(btn.pressed);
26758         });
26759         
26760         if (this.editor.btns.length > 0) {
26761             for (var i = 0; i<this.editor.btns.length; i++) {
26762                 children.push(this.editor.btns[i]);
26763             }
26764         }
26765         
26766         /*
26767         var cog = {
26768                 xtype: 'Button',
26769                 size : 'sm',
26770                 xns: Roo.bootstrap,
26771                 glyphicon : 'cog',
26772                 //html : 'submit'
26773                 menu : {
26774                     xtype: 'Menu',
26775                     xns: Roo.bootstrap,
26776                     items:  []
26777                 }
26778         };
26779         
26780         cog.menu.items.push({
26781             xtype :'MenuItem',
26782             xns: Roo.bootstrap,
26783             html : Clean styles,
26784             tagname : f,
26785             listeners : {
26786                 click : function()
26787                 {
26788                     editorcore.insertTag(this.tagname);
26789                     editor.focus();
26790                 }
26791             }
26792             
26793         });
26794        */
26795         
26796          
26797        this.xtype = 'NavSimplebar';
26798         
26799         for(var i=0;i< children.length;i++) {
26800             
26801             this.buttons.add(this.addxtypeChild(children[i]));
26802             
26803         }
26804         
26805         editor.on('editorevent', this.updateToolbar, this);
26806     },
26807     onBtnClick : function(id)
26808     {
26809        this.editorcore.relayCmd(id);
26810        this.editorcore.focus();
26811     },
26812     
26813     /**
26814      * Protected method that will not generally be called directly. It triggers
26815      * a toolbar update by reading the markup state of the current selection in the editor.
26816      */
26817     updateToolbar: function(){
26818
26819         if(!this.editorcore.activated){
26820             this.editor.onFirstFocus(); // is this neeed?
26821             return;
26822         }
26823
26824         var btns = this.buttons; 
26825         var doc = this.editorcore.doc;
26826         btns.get('bold').setActive(doc.queryCommandState('bold'));
26827         btns.get('italic').setActive(doc.queryCommandState('italic'));
26828         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26829         
26830         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26831         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26832         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26833         
26834         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26835         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26836          /*
26837         
26838         var ans = this.editorcore.getAllAncestors();
26839         if (this.formatCombo) {
26840             
26841             
26842             var store = this.formatCombo.store;
26843             this.formatCombo.setValue("");
26844             for (var i =0; i < ans.length;i++) {
26845                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26846                     // select it..
26847                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26848                     break;
26849                 }
26850             }
26851         }
26852         
26853         
26854         
26855         // hides menus... - so this cant be on a menu...
26856         Roo.bootstrap.MenuMgr.hideAll();
26857         */
26858         Roo.bootstrap.MenuMgr.hideAll();
26859         //this.editorsyncValue();
26860     },
26861     onFirstFocus: function() {
26862         this.buttons.each(function(item){
26863            item.enable();
26864         });
26865     },
26866     toggleSourceEdit : function(sourceEditMode){
26867         
26868           
26869         if(sourceEditMode){
26870             Roo.log("disabling buttons");
26871            this.buttons.each( function(item){
26872                 if(item.cmd != 'pencil'){
26873                     item.disable();
26874                 }
26875             });
26876           
26877         }else{
26878             Roo.log("enabling buttons");
26879             if(this.editorcore.initialized){
26880                 this.buttons.each( function(item){
26881                     item.enable();
26882                 });
26883             }
26884             
26885         }
26886         Roo.log("calling toggole on editor");
26887         // tell the editor that it's been pressed..
26888         this.editor.toggleSourceEdit(sourceEditMode);
26889        
26890     }
26891 });
26892
26893
26894
26895
26896  
26897 /*
26898  * - LGPL
26899  */
26900
26901 /**
26902  * @class Roo.bootstrap.Markdown
26903  * @extends Roo.bootstrap.TextArea
26904  * Bootstrap Showdown editable area
26905  * @cfg {string} content
26906  * 
26907  * @constructor
26908  * Create a new Showdown
26909  */
26910
26911 Roo.bootstrap.Markdown = function(config){
26912     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26913    
26914 };
26915
26916 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26917     
26918     editing :false,
26919     
26920     initEvents : function()
26921     {
26922         
26923         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26924         this.markdownEl = this.el.createChild({
26925             cls : 'roo-markdown-area'
26926         });
26927         this.inputEl().addClass('d-none');
26928         if (this.getValue() == '') {
26929             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26930             
26931         } else {
26932             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26933         }
26934         this.markdownEl.on('click', this.toggleTextEdit, this);
26935         this.on('blur', this.toggleTextEdit, this);
26936         this.on('specialkey', this.resizeTextArea, this);
26937     },
26938     
26939     toggleTextEdit : function()
26940     {
26941         var sh = this.markdownEl.getHeight();
26942         this.inputEl().addClass('d-none');
26943         this.markdownEl.addClass('d-none');
26944         if (!this.editing) {
26945             // show editor?
26946             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26947             this.inputEl().removeClass('d-none');
26948             this.inputEl().focus();
26949             this.editing = true;
26950             return;
26951         }
26952         // show showdown...
26953         this.updateMarkdown();
26954         this.markdownEl.removeClass('d-none');
26955         this.editing = false;
26956         return;
26957     },
26958     updateMarkdown : function()
26959     {
26960         if (this.getValue() == '') {
26961             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26962             return;
26963         }
26964  
26965         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26966     },
26967     
26968     resizeTextArea: function () {
26969         
26970         var sh = 100;
26971         Roo.log([sh, this.getValue().split("\n").length * 30]);
26972         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26973     },
26974     setValue : function(val)
26975     {
26976         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26977         if (!this.editing) {
26978             this.updateMarkdown();
26979         }
26980         
26981     },
26982     focus : function()
26983     {
26984         if (!this.editing) {
26985             this.toggleTextEdit();
26986         }
26987         
26988     }
26989
26990
26991 });
26992 /**
26993  * @class Roo.bootstrap.Table.AbstractSelectionModel
26994  * @extends Roo.util.Observable
26995  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26996  * implemented by descendant classes.  This class should not be directly instantiated.
26997  * @constructor
26998  */
26999 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27000     this.locked = false;
27001     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27002 };
27003
27004
27005 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27006     /** @ignore Called by the grid automatically. Do not call directly. */
27007     init : function(grid){
27008         this.grid = grid;
27009         this.initEvents();
27010     },
27011
27012     /**
27013      * Locks the selections.
27014      */
27015     lock : function(){
27016         this.locked = true;
27017     },
27018
27019     /**
27020      * Unlocks the selections.
27021      */
27022     unlock : function(){
27023         this.locked = false;
27024     },
27025
27026     /**
27027      * Returns true if the selections are locked.
27028      * @return {Boolean}
27029      */
27030     isLocked : function(){
27031         return this.locked;
27032     },
27033     
27034     
27035     initEvents : function ()
27036     {
27037         
27038     }
27039 });
27040 /**
27041  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27042  * @class Roo.bootstrap.Table.RowSelectionModel
27043  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27044  * It supports multiple selections and keyboard selection/navigation. 
27045  * @constructor
27046  * @param {Object} config
27047  */
27048
27049 Roo.bootstrap.Table.RowSelectionModel = function(config){
27050     Roo.apply(this, config);
27051     this.selections = new Roo.util.MixedCollection(false, function(o){
27052         return o.id;
27053     });
27054
27055     this.last = false;
27056     this.lastActive = false;
27057
27058     this.addEvents({
27059         /**
27060              * @event selectionchange
27061              * Fires when the selection changes
27062              * @param {SelectionModel} this
27063              */
27064             "selectionchange" : true,
27065         /**
27066              * @event afterselectionchange
27067              * Fires after the selection changes (eg. by key press or clicking)
27068              * @param {SelectionModel} this
27069              */
27070             "afterselectionchange" : true,
27071         /**
27072              * @event beforerowselect
27073              * Fires when a row is selected being selected, return false to cancel.
27074              * @param {SelectionModel} this
27075              * @param {Number} rowIndex The selected index
27076              * @param {Boolean} keepExisting False if other selections will be cleared
27077              */
27078             "beforerowselect" : true,
27079         /**
27080              * @event rowselect
27081              * Fires when a row is selected.
27082              * @param {SelectionModel} this
27083              * @param {Number} rowIndex The selected index
27084              * @param {Roo.data.Record} r The record
27085              */
27086             "rowselect" : true,
27087         /**
27088              * @event rowdeselect
27089              * Fires when a row is deselected.
27090              * @param {SelectionModel} this
27091              * @param {Number} rowIndex The selected index
27092              */
27093         "rowdeselect" : true
27094     });
27095     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27096     this.locked = false;
27097  };
27098
27099 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27100     /**
27101      * @cfg {Boolean} singleSelect
27102      * True to allow selection of only one row at a time (defaults to false)
27103      */
27104     singleSelect : false,
27105
27106     // private
27107     initEvents : function()
27108     {
27109
27110         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27111         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27112         //}else{ // allow click to work like normal
27113          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27114         //}
27115         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27116         this.grid.on("rowclick", this.handleMouseDown, this);
27117         
27118         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27119             "up" : function(e){
27120                 if(!e.shiftKey){
27121                     this.selectPrevious(e.shiftKey);
27122                 }else if(this.last !== false && this.lastActive !== false){
27123                     var last = this.last;
27124                     this.selectRange(this.last,  this.lastActive-1);
27125                     this.grid.getView().focusRow(this.lastActive);
27126                     if(last !== false){
27127                         this.last = last;
27128                     }
27129                 }else{
27130                     this.selectFirstRow();
27131                 }
27132                 this.fireEvent("afterselectionchange", this);
27133             },
27134             "down" : function(e){
27135                 if(!e.shiftKey){
27136                     this.selectNext(e.shiftKey);
27137                 }else if(this.last !== false && this.lastActive !== false){
27138                     var last = this.last;
27139                     this.selectRange(this.last,  this.lastActive+1);
27140                     this.grid.getView().focusRow(this.lastActive);
27141                     if(last !== false){
27142                         this.last = last;
27143                     }
27144                 }else{
27145                     this.selectFirstRow();
27146                 }
27147                 this.fireEvent("afterselectionchange", this);
27148             },
27149             scope: this
27150         });
27151         this.grid.store.on('load', function(){
27152             this.selections.clear();
27153         },this);
27154         /*
27155         var view = this.grid.view;
27156         view.on("refresh", this.onRefresh, this);
27157         view.on("rowupdated", this.onRowUpdated, this);
27158         view.on("rowremoved", this.onRemove, this);
27159         */
27160     },
27161
27162     // private
27163     onRefresh : function()
27164     {
27165         var ds = this.grid.store, i, v = this.grid.view;
27166         var s = this.selections;
27167         s.each(function(r){
27168             if((i = ds.indexOfId(r.id)) != -1){
27169                 v.onRowSelect(i);
27170             }else{
27171                 s.remove(r);
27172             }
27173         });
27174     },
27175
27176     // private
27177     onRemove : function(v, index, r){
27178         this.selections.remove(r);
27179     },
27180
27181     // private
27182     onRowUpdated : function(v, index, r){
27183         if(this.isSelected(r)){
27184             v.onRowSelect(index);
27185         }
27186     },
27187
27188     /**
27189      * Select records.
27190      * @param {Array} records The records to select
27191      * @param {Boolean} keepExisting (optional) True to keep existing selections
27192      */
27193     selectRecords : function(records, keepExisting)
27194     {
27195         if(!keepExisting){
27196             this.clearSelections();
27197         }
27198             var ds = this.grid.store;
27199         for(var i = 0, len = records.length; i < len; i++){
27200             this.selectRow(ds.indexOf(records[i]), true);
27201         }
27202     },
27203
27204     /**
27205      * Gets the number of selected rows.
27206      * @return {Number}
27207      */
27208     getCount : function(){
27209         return this.selections.length;
27210     },
27211
27212     /**
27213      * Selects the first row in the grid.
27214      */
27215     selectFirstRow : function(){
27216         this.selectRow(0);
27217     },
27218
27219     /**
27220      * Select the last row.
27221      * @param {Boolean} keepExisting (optional) True to keep existing selections
27222      */
27223     selectLastRow : function(keepExisting){
27224         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27225         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27226     },
27227
27228     /**
27229      * Selects the row immediately following the last selected row.
27230      * @param {Boolean} keepExisting (optional) True to keep existing selections
27231      */
27232     selectNext : function(keepExisting)
27233     {
27234             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27235             this.selectRow(this.last+1, keepExisting);
27236             this.grid.getView().focusRow(this.last);
27237         }
27238     },
27239
27240     /**
27241      * Selects the row that precedes the last selected row.
27242      * @param {Boolean} keepExisting (optional) True to keep existing selections
27243      */
27244     selectPrevious : function(keepExisting){
27245         if(this.last){
27246             this.selectRow(this.last-1, keepExisting);
27247             this.grid.getView().focusRow(this.last);
27248         }
27249     },
27250
27251     /**
27252      * Returns the selected records
27253      * @return {Array} Array of selected records
27254      */
27255     getSelections : function(){
27256         return [].concat(this.selections.items);
27257     },
27258
27259     /**
27260      * Returns the first selected record.
27261      * @return {Record}
27262      */
27263     getSelected : function(){
27264         return this.selections.itemAt(0);
27265     },
27266
27267
27268     /**
27269      * Clears all selections.
27270      */
27271     clearSelections : function(fast)
27272     {
27273         if(this.locked) {
27274             return;
27275         }
27276         if(fast !== true){
27277                 var ds = this.grid.store;
27278             var s = this.selections;
27279             s.each(function(r){
27280                 this.deselectRow(ds.indexOfId(r.id));
27281             }, this);
27282             s.clear();
27283         }else{
27284             this.selections.clear();
27285         }
27286         this.last = false;
27287     },
27288
27289
27290     /**
27291      * Selects all rows.
27292      */
27293     selectAll : function(){
27294         if(this.locked) {
27295             return;
27296         }
27297         this.selections.clear();
27298         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27299             this.selectRow(i, true);
27300         }
27301     },
27302
27303     /**
27304      * Returns True if there is a selection.
27305      * @return {Boolean}
27306      */
27307     hasSelection : function(){
27308         return this.selections.length > 0;
27309     },
27310
27311     /**
27312      * Returns True if the specified row is selected.
27313      * @param {Number/Record} record The record or index of the record to check
27314      * @return {Boolean}
27315      */
27316     isSelected : function(index){
27317             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27318         return (r && this.selections.key(r.id) ? true : false);
27319     },
27320
27321     /**
27322      * Returns True if the specified record id is selected.
27323      * @param {String} id The id of record to check
27324      * @return {Boolean}
27325      */
27326     isIdSelected : function(id){
27327         return (this.selections.key(id) ? true : false);
27328     },
27329
27330
27331     // private
27332     handleMouseDBClick : function(e, t){
27333         
27334     },
27335     // private
27336     handleMouseDown : function(e, t)
27337     {
27338             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27339         if(this.isLocked() || rowIndex < 0 ){
27340             return;
27341         };
27342         if(e.shiftKey && this.last !== false){
27343             var last = this.last;
27344             this.selectRange(last, rowIndex, e.ctrlKey);
27345             this.last = last; // reset the last
27346             t.focus();
27347     
27348         }else{
27349             var isSelected = this.isSelected(rowIndex);
27350             //Roo.log("select row:" + rowIndex);
27351             if(isSelected){
27352                 this.deselectRow(rowIndex);
27353             } else {
27354                         this.selectRow(rowIndex, true);
27355             }
27356     
27357             /*
27358                 if(e.button !== 0 && isSelected){
27359                 alert('rowIndex 2: ' + rowIndex);
27360                     view.focusRow(rowIndex);
27361                 }else if(e.ctrlKey && isSelected){
27362                     this.deselectRow(rowIndex);
27363                 }else if(!isSelected){
27364                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27365                     view.focusRow(rowIndex);
27366                 }
27367             */
27368         }
27369         this.fireEvent("afterselectionchange", this);
27370     },
27371     // private
27372     handleDragableRowClick :  function(grid, rowIndex, e) 
27373     {
27374         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27375             this.selectRow(rowIndex, false);
27376             grid.view.focusRow(rowIndex);
27377              this.fireEvent("afterselectionchange", this);
27378         }
27379     },
27380     
27381     /**
27382      * Selects multiple rows.
27383      * @param {Array} rows Array of the indexes of the row to select
27384      * @param {Boolean} keepExisting (optional) True to keep existing selections
27385      */
27386     selectRows : function(rows, keepExisting){
27387         if(!keepExisting){
27388             this.clearSelections();
27389         }
27390         for(var i = 0, len = rows.length; i < len; i++){
27391             this.selectRow(rows[i], true);
27392         }
27393     },
27394
27395     /**
27396      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27397      * @param {Number} startRow The index of the first row in the range
27398      * @param {Number} endRow The index of the last row in the range
27399      * @param {Boolean} keepExisting (optional) True to retain existing selections
27400      */
27401     selectRange : function(startRow, endRow, keepExisting){
27402         if(this.locked) {
27403             return;
27404         }
27405         if(!keepExisting){
27406             this.clearSelections();
27407         }
27408         if(startRow <= endRow){
27409             for(var i = startRow; i <= endRow; i++){
27410                 this.selectRow(i, true);
27411             }
27412         }else{
27413             for(var i = startRow; i >= endRow; i--){
27414                 this.selectRow(i, true);
27415             }
27416         }
27417     },
27418
27419     /**
27420      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27421      * @param {Number} startRow The index of the first row in the range
27422      * @param {Number} endRow The index of the last row in the range
27423      */
27424     deselectRange : function(startRow, endRow, preventViewNotify){
27425         if(this.locked) {
27426             return;
27427         }
27428         for(var i = startRow; i <= endRow; i++){
27429             this.deselectRow(i, preventViewNotify);
27430         }
27431     },
27432
27433     /**
27434      * Selects a row.
27435      * @param {Number} row The index of the row to select
27436      * @param {Boolean} keepExisting (optional) True to keep existing selections
27437      */
27438     selectRow : function(index, keepExisting, preventViewNotify)
27439     {
27440             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27441             return;
27442         }
27443         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27444             if(!keepExisting || this.singleSelect){
27445                 this.clearSelections();
27446             }
27447             
27448             var r = this.grid.store.getAt(index);
27449             //console.log('selectRow - record id :' + r.id);
27450             
27451             this.selections.add(r);
27452             this.last = this.lastActive = index;
27453             if(!preventViewNotify){
27454                 var proxy = new Roo.Element(
27455                                 this.grid.getRowDom(index)
27456                 );
27457                 proxy.addClass('bg-info info');
27458             }
27459             this.fireEvent("rowselect", this, index, r);
27460             this.fireEvent("selectionchange", this);
27461         }
27462     },
27463
27464     /**
27465      * Deselects a row.
27466      * @param {Number} row The index of the row to deselect
27467      */
27468     deselectRow : function(index, preventViewNotify)
27469     {
27470         if(this.locked) {
27471             return;
27472         }
27473         if(this.last == index){
27474             this.last = false;
27475         }
27476         if(this.lastActive == index){
27477             this.lastActive = false;
27478         }
27479         
27480         var r = this.grid.store.getAt(index);
27481         if (!r) {
27482             return;
27483         }
27484         
27485         this.selections.remove(r);
27486         //.console.log('deselectRow - record id :' + r.id);
27487         if(!preventViewNotify){
27488         
27489             var proxy = new Roo.Element(
27490                 this.grid.getRowDom(index)
27491             );
27492             proxy.removeClass('bg-info info');
27493         }
27494         this.fireEvent("rowdeselect", this, index);
27495         this.fireEvent("selectionchange", this);
27496     },
27497
27498     // private
27499     restoreLast : function(){
27500         if(this._last){
27501             this.last = this._last;
27502         }
27503     },
27504
27505     // private
27506     acceptsNav : function(row, col, cm){
27507         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27508     },
27509
27510     // private
27511     onEditorKey : function(field, e){
27512         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27513         if(k == e.TAB){
27514             e.stopEvent();
27515             ed.completeEdit();
27516             if(e.shiftKey){
27517                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27518             }else{
27519                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27520             }
27521         }else if(k == e.ENTER && !e.ctrlKey){
27522             e.stopEvent();
27523             ed.completeEdit();
27524             if(e.shiftKey){
27525                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27526             }else{
27527                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27528             }
27529         }else if(k == e.ESC){
27530             ed.cancelEdit();
27531         }
27532         if(newCell){
27533             g.startEditing(newCell[0], newCell[1]);
27534         }
27535     }
27536 });
27537 /*
27538  * Based on:
27539  * Ext JS Library 1.1.1
27540  * Copyright(c) 2006-2007, Ext JS, LLC.
27541  *
27542  * Originally Released Under LGPL - original licence link has changed is not relivant.
27543  *
27544  * Fork - LGPL
27545  * <script type="text/javascript">
27546  */
27547  
27548 /**
27549  * @class Roo.bootstrap.PagingToolbar
27550  * @extends Roo.bootstrap.NavSimplebar
27551  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27552  * @constructor
27553  * Create a new PagingToolbar
27554  * @param {Object} config The config object
27555  * @param {Roo.data.Store} store
27556  */
27557 Roo.bootstrap.PagingToolbar = function(config)
27558 {
27559     // old args format still supported... - xtype is prefered..
27560         // created from xtype...
27561     
27562     this.ds = config.dataSource;
27563     
27564     if (config.store && !this.ds) {
27565         this.store= Roo.factory(config.store, Roo.data);
27566         this.ds = this.store;
27567         this.ds.xmodule = this.xmodule || false;
27568     }
27569     
27570     this.toolbarItems = [];
27571     if (config.items) {
27572         this.toolbarItems = config.items;
27573     }
27574     
27575     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27576     
27577     this.cursor = 0;
27578     
27579     if (this.ds) { 
27580         this.bind(this.ds);
27581     }
27582     
27583     if (Roo.bootstrap.version == 4) {
27584         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27585     } else {
27586         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27587     }
27588     
27589 };
27590
27591 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27592     /**
27593      * @cfg {Roo.data.Store} dataSource
27594      * The underlying data store providing the paged data
27595      */
27596     /**
27597      * @cfg {String/HTMLElement/Element} container
27598      * container The id or element that will contain the toolbar
27599      */
27600     /**
27601      * @cfg {Boolean} displayInfo
27602      * True to display the displayMsg (defaults to false)
27603      */
27604     /**
27605      * @cfg {Number} pageSize
27606      * The number of records to display per page (defaults to 20)
27607      */
27608     pageSize: 20,
27609     /**
27610      * @cfg {String} displayMsg
27611      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27612      */
27613     displayMsg : 'Displaying {0} - {1} of {2}',
27614     /**
27615      * @cfg {String} emptyMsg
27616      * The message to display when no records are found (defaults to "No data to display")
27617      */
27618     emptyMsg : 'No data to display',
27619     /**
27620      * Customizable piece of the default paging text (defaults to "Page")
27621      * @type String
27622      */
27623     beforePageText : "Page",
27624     /**
27625      * Customizable piece of the default paging text (defaults to "of %0")
27626      * @type String
27627      */
27628     afterPageText : "of {0}",
27629     /**
27630      * Customizable piece of the default paging text (defaults to "First Page")
27631      * @type String
27632      */
27633     firstText : "First Page",
27634     /**
27635      * Customizable piece of the default paging text (defaults to "Previous Page")
27636      * @type String
27637      */
27638     prevText : "Previous Page",
27639     /**
27640      * Customizable piece of the default paging text (defaults to "Next Page")
27641      * @type String
27642      */
27643     nextText : "Next Page",
27644     /**
27645      * Customizable piece of the default paging text (defaults to "Last Page")
27646      * @type String
27647      */
27648     lastText : "Last Page",
27649     /**
27650      * Customizable piece of the default paging text (defaults to "Refresh")
27651      * @type String
27652      */
27653     refreshText : "Refresh",
27654
27655     buttons : false,
27656     // private
27657     onRender : function(ct, position) 
27658     {
27659         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27660         this.navgroup.parentId = this.id;
27661         this.navgroup.onRender(this.el, null);
27662         // add the buttons to the navgroup
27663         
27664         if(this.displayInfo){
27665             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27666             this.displayEl = this.el.select('.x-paging-info', true).first();
27667 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27668 //            this.displayEl = navel.el.select('span',true).first();
27669         }
27670         
27671         var _this = this;
27672         
27673         if(this.buttons){
27674             Roo.each(_this.buttons, function(e){ // this might need to use render????
27675                Roo.factory(e).render(_this.el);
27676             });
27677         }
27678             
27679         Roo.each(_this.toolbarItems, function(e) {
27680             _this.navgroup.addItem(e);
27681         });
27682         
27683         
27684         this.first = this.navgroup.addItem({
27685             tooltip: this.firstText,
27686             cls: "prev btn-outline-secondary",
27687             html : ' <i class="fa fa-step-backward"></i>',
27688             disabled: true,
27689             preventDefault: true,
27690             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27691         });
27692         
27693         this.prev =  this.navgroup.addItem({
27694             tooltip: this.prevText,
27695             cls: "prev btn-outline-secondary",
27696             html : ' <i class="fa fa-backward"></i>',
27697             disabled: true,
27698             preventDefault: true,
27699             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27700         });
27701     //this.addSeparator();
27702         
27703         
27704         var field = this.navgroup.addItem( {
27705             tagtype : 'span',
27706             cls : 'x-paging-position  btn-outline-secondary',
27707              disabled: true,
27708             html : this.beforePageText  +
27709                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27710                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27711          } ); //?? escaped?
27712         
27713         this.field = field.el.select('input', true).first();
27714         this.field.on("keydown", this.onPagingKeydown, this);
27715         this.field.on("focus", function(){this.dom.select();});
27716     
27717     
27718         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27719         //this.field.setHeight(18);
27720         //this.addSeparator();
27721         this.next = this.navgroup.addItem({
27722             tooltip: this.nextText,
27723             cls: "next btn-outline-secondary",
27724             html : ' <i class="fa fa-forward"></i>',
27725             disabled: true,
27726             preventDefault: true,
27727             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27728         });
27729         this.last = this.navgroup.addItem({
27730             tooltip: this.lastText,
27731             html : ' <i class="fa fa-step-forward"></i>',
27732             cls: "next btn-outline-secondary",
27733             disabled: true,
27734             preventDefault: true,
27735             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27736         });
27737     //this.addSeparator();
27738         this.loading = this.navgroup.addItem({
27739             tooltip: this.refreshText,
27740             cls: "btn-outline-secondary",
27741             html : ' <i class="fa fa-refresh"></i>',
27742             preventDefault: true,
27743             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27744         });
27745         
27746     },
27747
27748     // private
27749     updateInfo : function(){
27750         if(this.displayEl){
27751             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27752             var msg = count == 0 ?
27753                 this.emptyMsg :
27754                 String.format(
27755                     this.displayMsg,
27756                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27757                 );
27758             this.displayEl.update(msg);
27759         }
27760     },
27761
27762     // private
27763     onLoad : function(ds, r, o)
27764     {
27765         this.cursor = o.params && o.params.start ? o.params.start : 0;
27766         
27767         var d = this.getPageData(),
27768             ap = d.activePage,
27769             ps = d.pages;
27770         
27771         
27772         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27773         this.field.dom.value = ap;
27774         this.first.setDisabled(ap == 1);
27775         this.prev.setDisabled(ap == 1);
27776         this.next.setDisabled(ap == ps);
27777         this.last.setDisabled(ap == ps);
27778         this.loading.enable();
27779         this.updateInfo();
27780     },
27781
27782     // private
27783     getPageData : function(){
27784         var total = this.ds.getTotalCount();
27785         return {
27786             total : total,
27787             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27788             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27789         };
27790     },
27791
27792     // private
27793     onLoadError : function(){
27794         this.loading.enable();
27795     },
27796
27797     // private
27798     onPagingKeydown : function(e){
27799         var k = e.getKey();
27800         var d = this.getPageData();
27801         if(k == e.RETURN){
27802             var v = this.field.dom.value, pageNum;
27803             if(!v || isNaN(pageNum = parseInt(v, 10))){
27804                 this.field.dom.value = d.activePage;
27805                 return;
27806             }
27807             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27808             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27809             e.stopEvent();
27810         }
27811         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))
27812         {
27813           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27814           this.field.dom.value = pageNum;
27815           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27816           e.stopEvent();
27817         }
27818         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27819         {
27820           var v = this.field.dom.value, pageNum; 
27821           var increment = (e.shiftKey) ? 10 : 1;
27822           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27823                 increment *= -1;
27824           }
27825           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27826             this.field.dom.value = d.activePage;
27827             return;
27828           }
27829           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27830           {
27831             this.field.dom.value = parseInt(v, 10) + increment;
27832             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27833             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27834           }
27835           e.stopEvent();
27836         }
27837     },
27838
27839     // private
27840     beforeLoad : function(){
27841         if(this.loading){
27842             this.loading.disable();
27843         }
27844     },
27845
27846     // private
27847     onClick : function(which){
27848         
27849         var ds = this.ds;
27850         if (!ds) {
27851             return;
27852         }
27853         
27854         switch(which){
27855             case "first":
27856                 ds.load({params:{start: 0, limit: this.pageSize}});
27857             break;
27858             case "prev":
27859                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27860             break;
27861             case "next":
27862                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27863             break;
27864             case "last":
27865                 var total = ds.getTotalCount();
27866                 var extra = total % this.pageSize;
27867                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27868                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27869             break;
27870             case "refresh":
27871                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27872             break;
27873         }
27874     },
27875
27876     /**
27877      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27878      * @param {Roo.data.Store} store The data store to unbind
27879      */
27880     unbind : function(ds){
27881         ds.un("beforeload", this.beforeLoad, this);
27882         ds.un("load", this.onLoad, this);
27883         ds.un("loadexception", this.onLoadError, this);
27884         ds.un("remove", this.updateInfo, this);
27885         ds.un("add", this.updateInfo, this);
27886         this.ds = undefined;
27887     },
27888
27889     /**
27890      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27891      * @param {Roo.data.Store} store The data store to bind
27892      */
27893     bind : function(ds){
27894         ds.on("beforeload", this.beforeLoad, this);
27895         ds.on("load", this.onLoad, this);
27896         ds.on("loadexception", this.onLoadError, this);
27897         ds.on("remove", this.updateInfo, this);
27898         ds.on("add", this.updateInfo, this);
27899         this.ds = ds;
27900     }
27901 });/*
27902  * - LGPL
27903  *
27904  * element
27905  * 
27906  */
27907
27908 /**
27909  * @class Roo.bootstrap.MessageBar
27910  * @extends Roo.bootstrap.Component
27911  * Bootstrap MessageBar class
27912  * @cfg {String} html contents of the MessageBar
27913  * @cfg {String} weight (info | success | warning | danger) default info
27914  * @cfg {String} beforeClass insert the bar before the given class
27915  * @cfg {Boolean} closable (true | false) default false
27916  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27917  * 
27918  * @constructor
27919  * Create a new Element
27920  * @param {Object} config The config object
27921  */
27922
27923 Roo.bootstrap.MessageBar = function(config){
27924     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27925 };
27926
27927 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27928     
27929     html: '',
27930     weight: 'info',
27931     closable: false,
27932     fixed: false,
27933     beforeClass: 'bootstrap-sticky-wrap',
27934     
27935     getAutoCreate : function(){
27936         
27937         var cfg = {
27938             tag: 'div',
27939             cls: 'alert alert-dismissable alert-' + this.weight,
27940             cn: [
27941                 {
27942                     tag: 'span',
27943                     cls: 'message',
27944                     html: this.html || ''
27945                 }
27946             ]
27947         };
27948         
27949         if(this.fixed){
27950             cfg.cls += ' alert-messages-fixed';
27951         }
27952         
27953         if(this.closable){
27954             cfg.cn.push({
27955                 tag: 'button',
27956                 cls: 'close',
27957                 html: 'x'
27958             });
27959         }
27960         
27961         return cfg;
27962     },
27963     
27964     onRender : function(ct, position)
27965     {
27966         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27967         
27968         if(!this.el){
27969             var cfg = Roo.apply({},  this.getAutoCreate());
27970             cfg.id = Roo.id();
27971             
27972             if (this.cls) {
27973                 cfg.cls += ' ' + this.cls;
27974             }
27975             if (this.style) {
27976                 cfg.style = this.style;
27977             }
27978             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27979             
27980             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27981         }
27982         
27983         this.el.select('>button.close').on('click', this.hide, this);
27984         
27985     },
27986     
27987     show : function()
27988     {
27989         if (!this.rendered) {
27990             this.render();
27991         }
27992         
27993         this.el.show();
27994         
27995         this.fireEvent('show', this);
27996         
27997     },
27998     
27999     hide : function()
28000     {
28001         if (!this.rendered) {
28002             this.render();
28003         }
28004         
28005         this.el.hide();
28006         
28007         this.fireEvent('hide', this);
28008     },
28009     
28010     update : function()
28011     {
28012 //        var e = this.el.dom.firstChild;
28013 //        
28014 //        if(this.closable){
28015 //            e = e.nextSibling;
28016 //        }
28017 //        
28018 //        e.data = this.html || '';
28019
28020         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28021     }
28022    
28023 });
28024
28025  
28026
28027      /*
28028  * - LGPL
28029  *
28030  * Graph
28031  * 
28032  */
28033
28034
28035 /**
28036  * @class Roo.bootstrap.Graph
28037  * @extends Roo.bootstrap.Component
28038  * Bootstrap Graph class
28039 > Prameters
28040  -sm {number} sm 4
28041  -md {number} md 5
28042  @cfg {String} graphtype  bar | vbar | pie
28043  @cfg {number} g_x coodinator | centre x (pie)
28044  @cfg {number} g_y coodinator | centre y (pie)
28045  @cfg {number} g_r radius (pie)
28046  @cfg {number} g_height height of the chart (respected by all elements in the set)
28047  @cfg {number} g_width width of the chart (respected by all elements in the set)
28048  @cfg {Object} title The title of the chart
28049     
28050  -{Array}  values
28051  -opts (object) options for the chart 
28052      o {
28053      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28054      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28055      o vgutter (number)
28056      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.
28057      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28058      o to
28059      o stretch (boolean)
28060      o }
28061  -opts (object) options for the pie
28062      o{
28063      o cut
28064      o startAngle (number)
28065      o endAngle (number)
28066      } 
28067  *
28068  * @constructor
28069  * Create a new Input
28070  * @param {Object} config The config object
28071  */
28072
28073 Roo.bootstrap.Graph = function(config){
28074     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28075     
28076     this.addEvents({
28077         // img events
28078         /**
28079          * @event click
28080          * The img click event for the img.
28081          * @param {Roo.EventObject} e
28082          */
28083         "click" : true
28084     });
28085 };
28086
28087 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28088     
28089     sm: 4,
28090     md: 5,
28091     graphtype: 'bar',
28092     g_height: 250,
28093     g_width: 400,
28094     g_x: 50,
28095     g_y: 50,
28096     g_r: 30,
28097     opts:{
28098         //g_colors: this.colors,
28099         g_type: 'soft',
28100         g_gutter: '20%'
28101
28102     },
28103     title : false,
28104
28105     getAutoCreate : function(){
28106         
28107         var cfg = {
28108             tag: 'div',
28109             html : null
28110         };
28111         
28112         
28113         return  cfg;
28114     },
28115
28116     onRender : function(ct,position){
28117         
28118         
28119         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28120         
28121         if (typeof(Raphael) == 'undefined') {
28122             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28123             return;
28124         }
28125         
28126         this.raphael = Raphael(this.el.dom);
28127         
28128                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28129                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28130                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28131                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28132                 /*
28133                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28134                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28135                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28136                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28137                 
28138                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28139                 r.barchart(330, 10, 300, 220, data1);
28140                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28141                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28142                 */
28143                 
28144                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28145                 // r.barchart(30, 30, 560, 250,  xdata, {
28146                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28147                 //     axis : "0 0 1 1",
28148                 //     axisxlabels :  xdata
28149                 //     //yvalues : cols,
28150                    
28151                 // });
28152 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28153 //        
28154 //        this.load(null,xdata,{
28155 //                axis : "0 0 1 1",
28156 //                axisxlabels :  xdata
28157 //                });
28158
28159     },
28160
28161     load : function(graphtype,xdata,opts)
28162     {
28163         this.raphael.clear();
28164         if(!graphtype) {
28165             graphtype = this.graphtype;
28166         }
28167         if(!opts){
28168             opts = this.opts;
28169         }
28170         var r = this.raphael,
28171             fin = function () {
28172                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28173             },
28174             fout = function () {
28175                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28176             },
28177             pfin = function() {
28178                 this.sector.stop();
28179                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28180
28181                 if (this.label) {
28182                     this.label[0].stop();
28183                     this.label[0].attr({ r: 7.5 });
28184                     this.label[1].attr({ "font-weight": 800 });
28185                 }
28186             },
28187             pfout = function() {
28188                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28189
28190                 if (this.label) {
28191                     this.label[0].animate({ r: 5 }, 500, "bounce");
28192                     this.label[1].attr({ "font-weight": 400 });
28193                 }
28194             };
28195
28196         switch(graphtype){
28197             case 'bar':
28198                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28199                 break;
28200             case 'hbar':
28201                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28202                 break;
28203             case 'pie':
28204 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28205 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28206 //            
28207                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28208                 
28209                 break;
28210
28211         }
28212         
28213         if(this.title){
28214             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28215         }
28216         
28217     },
28218     
28219     setTitle: function(o)
28220     {
28221         this.title = o;
28222     },
28223     
28224     initEvents: function() {
28225         
28226         if(!this.href){
28227             this.el.on('click', this.onClick, this);
28228         }
28229     },
28230     
28231     onClick : function(e)
28232     {
28233         Roo.log('img onclick');
28234         this.fireEvent('click', this, e);
28235     }
28236    
28237 });
28238
28239  
28240 /*
28241  * - LGPL
28242  *
28243  * numberBox
28244  * 
28245  */
28246 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28247
28248 /**
28249  * @class Roo.bootstrap.dash.NumberBox
28250  * @extends Roo.bootstrap.Component
28251  * Bootstrap NumberBox class
28252  * @cfg {String} headline Box headline
28253  * @cfg {String} content Box content
28254  * @cfg {String} icon Box icon
28255  * @cfg {String} footer Footer text
28256  * @cfg {String} fhref Footer href
28257  * 
28258  * @constructor
28259  * Create a new NumberBox
28260  * @param {Object} config The config object
28261  */
28262
28263
28264 Roo.bootstrap.dash.NumberBox = function(config){
28265     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28266     
28267 };
28268
28269 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28270     
28271     headline : '',
28272     content : '',
28273     icon : '',
28274     footer : '',
28275     fhref : '',
28276     ficon : '',
28277     
28278     getAutoCreate : function(){
28279         
28280         var cfg = {
28281             tag : 'div',
28282             cls : 'small-box ',
28283             cn : [
28284                 {
28285                     tag : 'div',
28286                     cls : 'inner',
28287                     cn :[
28288                         {
28289                             tag : 'h3',
28290                             cls : 'roo-headline',
28291                             html : this.headline
28292                         },
28293                         {
28294                             tag : 'p',
28295                             cls : 'roo-content',
28296                             html : this.content
28297                         }
28298                     ]
28299                 }
28300             ]
28301         };
28302         
28303         if(this.icon){
28304             cfg.cn.push({
28305                 tag : 'div',
28306                 cls : 'icon',
28307                 cn :[
28308                     {
28309                         tag : 'i',
28310                         cls : 'ion ' + this.icon
28311                     }
28312                 ]
28313             });
28314         }
28315         
28316         if(this.footer){
28317             var footer = {
28318                 tag : 'a',
28319                 cls : 'small-box-footer',
28320                 href : this.fhref || '#',
28321                 html : this.footer
28322             };
28323             
28324             cfg.cn.push(footer);
28325             
28326         }
28327         
28328         return  cfg;
28329     },
28330
28331     onRender : function(ct,position){
28332         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28333
28334
28335        
28336                 
28337     },
28338
28339     setHeadline: function (value)
28340     {
28341         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28342     },
28343     
28344     setFooter: function (value, href)
28345     {
28346         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28347         
28348         if(href){
28349             this.el.select('a.small-box-footer',true).first().attr('href', href);
28350         }
28351         
28352     },
28353
28354     setContent: function (value)
28355     {
28356         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28357     },
28358
28359     initEvents: function() 
28360     {   
28361         
28362     }
28363     
28364 });
28365
28366  
28367 /*
28368  * - LGPL
28369  *
28370  * TabBox
28371  * 
28372  */
28373 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28374
28375 /**
28376  * @class Roo.bootstrap.dash.TabBox
28377  * @extends Roo.bootstrap.Component
28378  * Bootstrap TabBox class
28379  * @cfg {String} title Title of the TabBox
28380  * @cfg {String} icon Icon of the TabBox
28381  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28382  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28383  * 
28384  * @constructor
28385  * Create a new TabBox
28386  * @param {Object} config The config object
28387  */
28388
28389
28390 Roo.bootstrap.dash.TabBox = function(config){
28391     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28392     this.addEvents({
28393         // raw events
28394         /**
28395          * @event addpane
28396          * When a pane is added
28397          * @param {Roo.bootstrap.dash.TabPane} pane
28398          */
28399         "addpane" : true,
28400         /**
28401          * @event activatepane
28402          * When a pane is activated
28403          * @param {Roo.bootstrap.dash.TabPane} pane
28404          */
28405         "activatepane" : true
28406         
28407          
28408     });
28409     
28410     this.panes = [];
28411 };
28412
28413 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28414
28415     title : '',
28416     icon : false,
28417     showtabs : true,
28418     tabScrollable : false,
28419     
28420     getChildContainer : function()
28421     {
28422         return this.el.select('.tab-content', true).first();
28423     },
28424     
28425     getAutoCreate : function(){
28426         
28427         var header = {
28428             tag: 'li',
28429             cls: 'pull-left header',
28430             html: this.title,
28431             cn : []
28432         };
28433         
28434         if(this.icon){
28435             header.cn.push({
28436                 tag: 'i',
28437                 cls: 'fa ' + this.icon
28438             });
28439         }
28440         
28441         var h = {
28442             tag: 'ul',
28443             cls: 'nav nav-tabs pull-right',
28444             cn: [
28445                 header
28446             ]
28447         };
28448         
28449         if(this.tabScrollable){
28450             h = {
28451                 tag: 'div',
28452                 cls: 'tab-header',
28453                 cn: [
28454                     {
28455                         tag: 'ul',
28456                         cls: 'nav nav-tabs pull-right',
28457                         cn: [
28458                             header
28459                         ]
28460                     }
28461                 ]
28462             };
28463         }
28464         
28465         var cfg = {
28466             tag: 'div',
28467             cls: 'nav-tabs-custom',
28468             cn: [
28469                 h,
28470                 {
28471                     tag: 'div',
28472                     cls: 'tab-content no-padding',
28473                     cn: []
28474                 }
28475             ]
28476         };
28477
28478         return  cfg;
28479     },
28480     initEvents : function()
28481     {
28482         //Roo.log('add add pane handler');
28483         this.on('addpane', this.onAddPane, this);
28484     },
28485      /**
28486      * Updates the box title
28487      * @param {String} html to set the title to.
28488      */
28489     setTitle : function(value)
28490     {
28491         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28492     },
28493     onAddPane : function(pane)
28494     {
28495         this.panes.push(pane);
28496         //Roo.log('addpane');
28497         //Roo.log(pane);
28498         // tabs are rendere left to right..
28499         if(!this.showtabs){
28500             return;
28501         }
28502         
28503         var ctr = this.el.select('.nav-tabs', true).first();
28504          
28505          
28506         var existing = ctr.select('.nav-tab',true);
28507         var qty = existing.getCount();;
28508         
28509         
28510         var tab = ctr.createChild({
28511             tag : 'li',
28512             cls : 'nav-tab' + (qty ? '' : ' active'),
28513             cn : [
28514                 {
28515                     tag : 'a',
28516                     href:'#',
28517                     html : pane.title
28518                 }
28519             ]
28520         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28521         pane.tab = tab;
28522         
28523         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28524         if (!qty) {
28525             pane.el.addClass('active');
28526         }
28527         
28528                 
28529     },
28530     onTabClick : function(ev,un,ob,pane)
28531     {
28532         //Roo.log('tab - prev default');
28533         ev.preventDefault();
28534         
28535         
28536         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28537         pane.tab.addClass('active');
28538         //Roo.log(pane.title);
28539         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28540         // technically we should have a deactivate event.. but maybe add later.
28541         // and it should not de-activate the selected tab...
28542         this.fireEvent('activatepane', pane);
28543         pane.el.addClass('active');
28544         pane.fireEvent('activate');
28545         
28546         
28547     },
28548     
28549     getActivePane : function()
28550     {
28551         var r = false;
28552         Roo.each(this.panes, function(p) {
28553             if(p.el.hasClass('active')){
28554                 r = p;
28555                 return false;
28556             }
28557             
28558             return;
28559         });
28560         
28561         return r;
28562     }
28563     
28564     
28565 });
28566
28567  
28568 /*
28569  * - LGPL
28570  *
28571  * Tab pane
28572  * 
28573  */
28574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28575 /**
28576  * @class Roo.bootstrap.TabPane
28577  * @extends Roo.bootstrap.Component
28578  * Bootstrap TabPane class
28579  * @cfg {Boolean} active (false | true) Default false
28580  * @cfg {String} title title of panel
28581
28582  * 
28583  * @constructor
28584  * Create a new TabPane
28585  * @param {Object} config The config object
28586  */
28587
28588 Roo.bootstrap.dash.TabPane = function(config){
28589     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28590     
28591     this.addEvents({
28592         // raw events
28593         /**
28594          * @event activate
28595          * When a pane is activated
28596          * @param {Roo.bootstrap.dash.TabPane} pane
28597          */
28598         "activate" : true
28599          
28600     });
28601 };
28602
28603 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28604     
28605     active : false,
28606     title : '',
28607     
28608     // the tabBox that this is attached to.
28609     tab : false,
28610      
28611     getAutoCreate : function() 
28612     {
28613         var cfg = {
28614             tag: 'div',
28615             cls: 'tab-pane'
28616         };
28617         
28618         if(this.active){
28619             cfg.cls += ' active';
28620         }
28621         
28622         return cfg;
28623     },
28624     initEvents  : function()
28625     {
28626         //Roo.log('trigger add pane handler');
28627         this.parent().fireEvent('addpane', this)
28628     },
28629     
28630      /**
28631      * Updates the tab title 
28632      * @param {String} html to set the title to.
28633      */
28634     setTitle: function(str)
28635     {
28636         if (!this.tab) {
28637             return;
28638         }
28639         this.title = str;
28640         this.tab.select('a', true).first().dom.innerHTML = str;
28641         
28642     }
28643     
28644     
28645     
28646 });
28647
28648  
28649
28650
28651  /*
28652  * - LGPL
28653  *
28654  * menu
28655  * 
28656  */
28657 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28658
28659 /**
28660  * @class Roo.bootstrap.menu.Menu
28661  * @extends Roo.bootstrap.Component
28662  * Bootstrap Menu class - container for Menu
28663  * @cfg {String} html Text of the menu
28664  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28665  * @cfg {String} icon Font awesome icon
28666  * @cfg {String} pos Menu align to (top | bottom) default bottom
28667  * 
28668  * 
28669  * @constructor
28670  * Create a new Menu
28671  * @param {Object} config The config object
28672  */
28673
28674
28675 Roo.bootstrap.menu.Menu = function(config){
28676     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28677     
28678     this.addEvents({
28679         /**
28680          * @event beforeshow
28681          * Fires before this menu is displayed
28682          * @param {Roo.bootstrap.menu.Menu} this
28683          */
28684         beforeshow : true,
28685         /**
28686          * @event beforehide
28687          * Fires before this menu is hidden
28688          * @param {Roo.bootstrap.menu.Menu} this
28689          */
28690         beforehide : true,
28691         /**
28692          * @event show
28693          * Fires after this menu is displayed
28694          * @param {Roo.bootstrap.menu.Menu} this
28695          */
28696         show : true,
28697         /**
28698          * @event hide
28699          * Fires after this menu is hidden
28700          * @param {Roo.bootstrap.menu.Menu} this
28701          */
28702         hide : true,
28703         /**
28704          * @event click
28705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28706          * @param {Roo.bootstrap.menu.Menu} this
28707          * @param {Roo.EventObject} e
28708          */
28709         click : true
28710     });
28711     
28712 };
28713
28714 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28715     
28716     submenu : false,
28717     html : '',
28718     weight : 'default',
28719     icon : false,
28720     pos : 'bottom',
28721     
28722     
28723     getChildContainer : function() {
28724         if(this.isSubMenu){
28725             return this.el;
28726         }
28727         
28728         return this.el.select('ul.dropdown-menu', true).first();  
28729     },
28730     
28731     getAutoCreate : function()
28732     {
28733         var text = [
28734             {
28735                 tag : 'span',
28736                 cls : 'roo-menu-text',
28737                 html : this.html
28738             }
28739         ];
28740         
28741         if(this.icon){
28742             text.unshift({
28743                 tag : 'i',
28744                 cls : 'fa ' + this.icon
28745             })
28746         }
28747         
28748         
28749         var cfg = {
28750             tag : 'div',
28751             cls : 'btn-group',
28752             cn : [
28753                 {
28754                     tag : 'button',
28755                     cls : 'dropdown-button btn btn-' + this.weight,
28756                     cn : text
28757                 },
28758                 {
28759                     tag : 'button',
28760                     cls : 'dropdown-toggle btn btn-' + this.weight,
28761                     cn : [
28762                         {
28763                             tag : 'span',
28764                             cls : 'caret'
28765                         }
28766                     ]
28767                 },
28768                 {
28769                     tag : 'ul',
28770                     cls : 'dropdown-menu'
28771                 }
28772             ]
28773             
28774         };
28775         
28776         if(this.pos == 'top'){
28777             cfg.cls += ' dropup';
28778         }
28779         
28780         if(this.isSubMenu){
28781             cfg = {
28782                 tag : 'ul',
28783                 cls : 'dropdown-menu'
28784             }
28785         }
28786         
28787         return cfg;
28788     },
28789     
28790     onRender : function(ct, position)
28791     {
28792         this.isSubMenu = ct.hasClass('dropdown-submenu');
28793         
28794         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28795     },
28796     
28797     initEvents : function() 
28798     {
28799         if(this.isSubMenu){
28800             return;
28801         }
28802         
28803         this.hidden = true;
28804         
28805         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28806         this.triggerEl.on('click', this.onTriggerPress, this);
28807         
28808         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28809         this.buttonEl.on('click', this.onClick, this);
28810         
28811     },
28812     
28813     list : function()
28814     {
28815         if(this.isSubMenu){
28816             return this.el;
28817         }
28818         
28819         return this.el.select('ul.dropdown-menu', true).first();
28820     },
28821     
28822     onClick : function(e)
28823     {
28824         this.fireEvent("click", this, e);
28825     },
28826     
28827     onTriggerPress  : function(e)
28828     {   
28829         if (this.isVisible()) {
28830             this.hide();
28831         } else {
28832             this.show();
28833         }
28834     },
28835     
28836     isVisible : function(){
28837         return !this.hidden;
28838     },
28839     
28840     show : function()
28841     {
28842         this.fireEvent("beforeshow", this);
28843         
28844         this.hidden = false;
28845         this.el.addClass('open');
28846         
28847         Roo.get(document).on("mouseup", this.onMouseUp, this);
28848         
28849         this.fireEvent("show", this);
28850         
28851         
28852     },
28853     
28854     hide : function()
28855     {
28856         this.fireEvent("beforehide", this);
28857         
28858         this.hidden = true;
28859         this.el.removeClass('open');
28860         
28861         Roo.get(document).un("mouseup", this.onMouseUp);
28862         
28863         this.fireEvent("hide", this);
28864     },
28865     
28866     onMouseUp : function()
28867     {
28868         this.hide();
28869     }
28870     
28871 });
28872
28873  
28874  /*
28875  * - LGPL
28876  *
28877  * menu item
28878  * 
28879  */
28880 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28881
28882 /**
28883  * @class Roo.bootstrap.menu.Item
28884  * @extends Roo.bootstrap.Component
28885  * Bootstrap MenuItem class
28886  * @cfg {Boolean} submenu (true | false) default false
28887  * @cfg {String} html text of the item
28888  * @cfg {String} href the link
28889  * @cfg {Boolean} disable (true | false) default false
28890  * @cfg {Boolean} preventDefault (true | false) default true
28891  * @cfg {String} icon Font awesome icon
28892  * @cfg {String} pos Submenu align to (left | right) default right 
28893  * 
28894  * 
28895  * @constructor
28896  * Create a new Item
28897  * @param {Object} config The config object
28898  */
28899
28900
28901 Roo.bootstrap.menu.Item = function(config){
28902     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28903     this.addEvents({
28904         /**
28905          * @event mouseover
28906          * Fires when the mouse is hovering over this menu
28907          * @param {Roo.bootstrap.menu.Item} this
28908          * @param {Roo.EventObject} e
28909          */
28910         mouseover : true,
28911         /**
28912          * @event mouseout
28913          * Fires when the mouse exits this menu
28914          * @param {Roo.bootstrap.menu.Item} this
28915          * @param {Roo.EventObject} e
28916          */
28917         mouseout : true,
28918         // raw events
28919         /**
28920          * @event click
28921          * The raw click event for the entire grid.
28922          * @param {Roo.EventObject} e
28923          */
28924         click : true
28925     });
28926 };
28927
28928 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28929     
28930     submenu : false,
28931     href : '',
28932     html : '',
28933     preventDefault: true,
28934     disable : false,
28935     icon : false,
28936     pos : 'right',
28937     
28938     getAutoCreate : function()
28939     {
28940         var text = [
28941             {
28942                 tag : 'span',
28943                 cls : 'roo-menu-item-text',
28944                 html : this.html
28945             }
28946         ];
28947         
28948         if(this.icon){
28949             text.unshift({
28950                 tag : 'i',
28951                 cls : 'fa ' + this.icon
28952             })
28953         }
28954         
28955         var cfg = {
28956             tag : 'li',
28957             cn : [
28958                 {
28959                     tag : 'a',
28960                     href : this.href || '#',
28961                     cn : text
28962                 }
28963             ]
28964         };
28965         
28966         if(this.disable){
28967             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28968         }
28969         
28970         if(this.submenu){
28971             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28972             
28973             if(this.pos == 'left'){
28974                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28975             }
28976         }
28977         
28978         return cfg;
28979     },
28980     
28981     initEvents : function() 
28982     {
28983         this.el.on('mouseover', this.onMouseOver, this);
28984         this.el.on('mouseout', this.onMouseOut, this);
28985         
28986         this.el.select('a', true).first().on('click', this.onClick, this);
28987         
28988     },
28989     
28990     onClick : function(e)
28991     {
28992         if(this.preventDefault){
28993             e.preventDefault();
28994         }
28995         
28996         this.fireEvent("click", this, e);
28997     },
28998     
28999     onMouseOver : function(e)
29000     {
29001         if(this.submenu && this.pos == 'left'){
29002             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29003         }
29004         
29005         this.fireEvent("mouseover", this, e);
29006     },
29007     
29008     onMouseOut : function(e)
29009     {
29010         this.fireEvent("mouseout", this, e);
29011     }
29012 });
29013
29014  
29015
29016  /*
29017  * - LGPL
29018  *
29019  * menu separator
29020  * 
29021  */
29022 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29023
29024 /**
29025  * @class Roo.bootstrap.menu.Separator
29026  * @extends Roo.bootstrap.Component
29027  * Bootstrap Separator class
29028  * 
29029  * @constructor
29030  * Create a new Separator
29031  * @param {Object} config The config object
29032  */
29033
29034
29035 Roo.bootstrap.menu.Separator = function(config){
29036     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29037 };
29038
29039 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29040     
29041     getAutoCreate : function(){
29042         var cfg = {
29043             tag : 'li',
29044             cls: 'dropdown-divider divider'
29045         };
29046         
29047         return cfg;
29048     }
29049    
29050 });
29051
29052  
29053
29054  /*
29055  * - LGPL
29056  *
29057  * Tooltip
29058  * 
29059  */
29060
29061 /**
29062  * @class Roo.bootstrap.Tooltip
29063  * Bootstrap Tooltip class
29064  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29065  * to determine which dom element triggers the tooltip.
29066  * 
29067  * It needs to add support for additional attributes like tooltip-position
29068  * 
29069  * @constructor
29070  * Create a new Toolti
29071  * @param {Object} config The config object
29072  */
29073
29074 Roo.bootstrap.Tooltip = function(config){
29075     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29076     
29077     this.alignment = Roo.bootstrap.Tooltip.alignment;
29078     
29079     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29080         this.alignment = config.alignment;
29081     }
29082     
29083 };
29084
29085 Roo.apply(Roo.bootstrap.Tooltip, {
29086     /**
29087      * @function init initialize tooltip monitoring.
29088      * @static
29089      */
29090     currentEl : false,
29091     currentTip : false,
29092     currentRegion : false,
29093     
29094     //  init : delay?
29095     
29096     init : function()
29097     {
29098         Roo.get(document).on('mouseover', this.enter ,this);
29099         Roo.get(document).on('mouseout', this.leave, this);
29100          
29101         
29102         this.currentTip = new Roo.bootstrap.Tooltip();
29103     },
29104     
29105     enter : function(ev)
29106     {
29107         var dom = ev.getTarget();
29108         
29109         //Roo.log(['enter',dom]);
29110         var el = Roo.fly(dom);
29111         if (this.currentEl) {
29112             //Roo.log(dom);
29113             //Roo.log(this.currentEl);
29114             //Roo.log(this.currentEl.contains(dom));
29115             if (this.currentEl == el) {
29116                 return;
29117             }
29118             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29119                 return;
29120             }
29121
29122         }
29123         
29124         if (this.currentTip.el) {
29125             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29126         }    
29127         //Roo.log(ev);
29128         
29129         if(!el || el.dom == document){
29130             return;
29131         }
29132         
29133         var bindEl = el; 
29134         var pel = false;
29135         if (!el.attr('tooltip')) {
29136             pel = el.findParent("[tooltip]");
29137             if (pel) {
29138                 bindEl = Roo.get(pel);
29139             }
29140         }
29141         
29142        
29143         
29144         // you can not look for children, as if el is the body.. then everythign is the child..
29145         if (!pel && !el.attr('tooltip')) { //
29146             if (!el.select("[tooltip]").elements.length) {
29147                 return;
29148             }
29149             // is the mouse over this child...?
29150             bindEl = el.select("[tooltip]").first();
29151             var xy = ev.getXY();
29152             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29153                 //Roo.log("not in region.");
29154                 return;
29155             }
29156             //Roo.log("child element over..");
29157             
29158         }
29159         this.currentEl = el;
29160         this.currentTip.bind(bindEl);
29161         this.currentRegion = Roo.lib.Region.getRegion(dom);
29162         this.currentTip.enter();
29163         
29164     },
29165     leave : function(ev)
29166     {
29167         var dom = ev.getTarget();
29168         //Roo.log(['leave',dom]);
29169         if (!this.currentEl) {
29170             return;
29171         }
29172         
29173         
29174         if (dom != this.currentEl.dom) {
29175             return;
29176         }
29177         var xy = ev.getXY();
29178         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29179             return;
29180         }
29181         // only activate leave if mouse cursor is outside... bounding box..
29182         
29183         
29184         
29185         
29186         if (this.currentTip) {
29187             this.currentTip.leave();
29188         }
29189         //Roo.log('clear currentEl');
29190         this.currentEl = false;
29191         
29192         
29193     },
29194     alignment : {
29195         'left' : ['r-l', [-2,0], 'right'],
29196         'right' : ['l-r', [2,0], 'left'],
29197         'bottom' : ['t-b', [0,2], 'top'],
29198         'top' : [ 'b-t', [0,-2], 'bottom']
29199     }
29200     
29201 });
29202
29203
29204 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29205     
29206     
29207     bindEl : false,
29208     
29209     delay : null, // can be { show : 300 , hide: 500}
29210     
29211     timeout : null,
29212     
29213     hoverState : null, //???
29214     
29215     placement : 'bottom', 
29216     
29217     alignment : false,
29218     
29219     getAutoCreate : function(){
29220     
29221         var cfg = {
29222            cls : 'tooltip',   
29223            role : 'tooltip',
29224            cn : [
29225                 {
29226                     cls : 'tooltip-arrow arrow'
29227                 },
29228                 {
29229                     cls : 'tooltip-inner'
29230                 }
29231            ]
29232         };
29233         
29234         return cfg;
29235     },
29236     bind : function(el)
29237     {
29238         this.bindEl = el;
29239     },
29240     
29241     initEvents : function()
29242     {
29243         this.arrowEl = this.el.select('.arrow', true).first();
29244         this.innerEl = this.el.select('.tooltip-inner', true).first();
29245     },
29246     
29247     enter : function () {
29248        
29249         if (this.timeout != null) {
29250             clearTimeout(this.timeout);
29251         }
29252         
29253         this.hoverState = 'in';
29254          //Roo.log("enter - show");
29255         if (!this.delay || !this.delay.show) {
29256             this.show();
29257             return;
29258         }
29259         var _t = this;
29260         this.timeout = setTimeout(function () {
29261             if (_t.hoverState == 'in') {
29262                 _t.show();
29263             }
29264         }, this.delay.show);
29265     },
29266     leave : function()
29267     {
29268         clearTimeout(this.timeout);
29269     
29270         this.hoverState = 'out';
29271          if (!this.delay || !this.delay.hide) {
29272             this.hide();
29273             return;
29274         }
29275        
29276         var _t = this;
29277         this.timeout = setTimeout(function () {
29278             //Roo.log("leave - timeout");
29279             
29280             if (_t.hoverState == 'out') {
29281                 _t.hide();
29282                 Roo.bootstrap.Tooltip.currentEl = false;
29283             }
29284         }, delay);
29285     },
29286     
29287     show : function (msg)
29288     {
29289         if (!this.el) {
29290             this.render(document.body);
29291         }
29292         // set content.
29293         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29294         
29295         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29296         
29297         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29298         
29299         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29300                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29301         
29302         var placement = typeof this.placement == 'function' ?
29303             this.placement.call(this, this.el, on_el) :
29304             this.placement;
29305             
29306         var autoToken = /\s?auto?\s?/i;
29307         var autoPlace = autoToken.test(placement);
29308         if (autoPlace) {
29309             placement = placement.replace(autoToken, '') || 'top';
29310         }
29311         
29312         //this.el.detach()
29313         //this.el.setXY([0,0]);
29314         this.el.show();
29315         //this.el.dom.style.display='block';
29316         
29317         //this.el.appendTo(on_el);
29318         
29319         var p = this.getPosition();
29320         var box = this.el.getBox();
29321         
29322         if (autoPlace) {
29323             // fixme..
29324         }
29325         
29326         var align = this.alignment[placement];
29327         
29328         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29329         
29330         if(placement == 'top' || placement == 'bottom'){
29331             if(xy[0] < 0){
29332                 placement = 'right';
29333             }
29334             
29335             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29336                 placement = 'left';
29337             }
29338             
29339             var scroll = Roo.select('body', true).first().getScroll();
29340             
29341             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29342                 placement = 'top';
29343             }
29344             
29345             align = this.alignment[placement];
29346             
29347             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29348             
29349         }
29350         
29351         var elems = document.getElementsByTagName('div');
29352         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29353         for (var i = 0; i < elems.length; i++) {
29354           var zindex = Number.parseInt(
29355                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29356                 10
29357           );
29358           if (zindex > highest) {
29359             highest = zindex;
29360           }
29361         }
29362         
29363         
29364         
29365         this.el.dom.style.zIndex = highest;
29366         
29367         this.el.alignTo(this.bindEl, align[0],align[1]);
29368         //var arrow = this.el.select('.arrow',true).first();
29369         //arrow.set(align[2], 
29370         
29371         this.el.addClass(placement);
29372         this.el.addClass("bs-tooltip-"+ placement);
29373         
29374         this.el.addClass('in fade show');
29375         
29376         this.hoverState = null;
29377         
29378         if (this.el.hasClass('fade')) {
29379             // fade it?
29380         }
29381         
29382         
29383         
29384         
29385         
29386     },
29387     hide : function()
29388     {
29389          
29390         if (!this.el) {
29391             return;
29392         }
29393         //this.el.setXY([0,0]);
29394         this.el.removeClass(['show', 'in']);
29395         //this.el.hide();
29396         
29397     }
29398     
29399 });
29400  
29401
29402  /*
29403  * - LGPL
29404  *
29405  * Location Picker
29406  * 
29407  */
29408
29409 /**
29410  * @class Roo.bootstrap.LocationPicker
29411  * @extends Roo.bootstrap.Component
29412  * Bootstrap LocationPicker class
29413  * @cfg {Number} latitude Position when init default 0
29414  * @cfg {Number} longitude Position when init default 0
29415  * @cfg {Number} zoom default 15
29416  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29417  * @cfg {Boolean} mapTypeControl default false
29418  * @cfg {Boolean} disableDoubleClickZoom default false
29419  * @cfg {Boolean} scrollwheel default true
29420  * @cfg {Boolean} streetViewControl default false
29421  * @cfg {Number} radius default 0
29422  * @cfg {String} locationName
29423  * @cfg {Boolean} draggable default true
29424  * @cfg {Boolean} enableAutocomplete default false
29425  * @cfg {Boolean} enableReverseGeocode default true
29426  * @cfg {String} markerTitle
29427  * 
29428  * @constructor
29429  * Create a new LocationPicker
29430  * @param {Object} config The config object
29431  */
29432
29433
29434 Roo.bootstrap.LocationPicker = function(config){
29435     
29436     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29437     
29438     this.addEvents({
29439         /**
29440          * @event initial
29441          * Fires when the picker initialized.
29442          * @param {Roo.bootstrap.LocationPicker} this
29443          * @param {Google Location} location
29444          */
29445         initial : true,
29446         /**
29447          * @event positionchanged
29448          * Fires when the picker position changed.
29449          * @param {Roo.bootstrap.LocationPicker} this
29450          * @param {Google Location} location
29451          */
29452         positionchanged : true,
29453         /**
29454          * @event resize
29455          * Fires when the map resize.
29456          * @param {Roo.bootstrap.LocationPicker} this
29457          */
29458         resize : true,
29459         /**
29460          * @event show
29461          * Fires when the map show.
29462          * @param {Roo.bootstrap.LocationPicker} this
29463          */
29464         show : true,
29465         /**
29466          * @event hide
29467          * Fires when the map hide.
29468          * @param {Roo.bootstrap.LocationPicker} this
29469          */
29470         hide : true,
29471         /**
29472          * @event mapClick
29473          * Fires when click the map.
29474          * @param {Roo.bootstrap.LocationPicker} this
29475          * @param {Map event} e
29476          */
29477         mapClick : true,
29478         /**
29479          * @event mapRightClick
29480          * Fires when right click the map.
29481          * @param {Roo.bootstrap.LocationPicker} this
29482          * @param {Map event} e
29483          */
29484         mapRightClick : true,
29485         /**
29486          * @event markerClick
29487          * Fires when click the marker.
29488          * @param {Roo.bootstrap.LocationPicker} this
29489          * @param {Map event} e
29490          */
29491         markerClick : true,
29492         /**
29493          * @event markerRightClick
29494          * Fires when right click the marker.
29495          * @param {Roo.bootstrap.LocationPicker} this
29496          * @param {Map event} e
29497          */
29498         markerRightClick : true,
29499         /**
29500          * @event OverlayViewDraw
29501          * Fires when OverlayView Draw
29502          * @param {Roo.bootstrap.LocationPicker} this
29503          */
29504         OverlayViewDraw : true,
29505         /**
29506          * @event OverlayViewOnAdd
29507          * Fires when OverlayView Draw
29508          * @param {Roo.bootstrap.LocationPicker} this
29509          */
29510         OverlayViewOnAdd : true,
29511         /**
29512          * @event OverlayViewOnRemove
29513          * Fires when OverlayView Draw
29514          * @param {Roo.bootstrap.LocationPicker} this
29515          */
29516         OverlayViewOnRemove : true,
29517         /**
29518          * @event OverlayViewShow
29519          * Fires when OverlayView Draw
29520          * @param {Roo.bootstrap.LocationPicker} this
29521          * @param {Pixel} cpx
29522          */
29523         OverlayViewShow : true,
29524         /**
29525          * @event OverlayViewHide
29526          * Fires when OverlayView Draw
29527          * @param {Roo.bootstrap.LocationPicker} this
29528          */
29529         OverlayViewHide : true,
29530         /**
29531          * @event loadexception
29532          * Fires when load google lib failed.
29533          * @param {Roo.bootstrap.LocationPicker} this
29534          */
29535         loadexception : true
29536     });
29537         
29538 };
29539
29540 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29541     
29542     gMapContext: false,
29543     
29544     latitude: 0,
29545     longitude: 0,
29546     zoom: 15,
29547     mapTypeId: false,
29548     mapTypeControl: false,
29549     disableDoubleClickZoom: false,
29550     scrollwheel: true,
29551     streetViewControl: false,
29552     radius: 0,
29553     locationName: '',
29554     draggable: true,
29555     enableAutocomplete: false,
29556     enableReverseGeocode: true,
29557     markerTitle: '',
29558     
29559     getAutoCreate: function()
29560     {
29561
29562         var cfg = {
29563             tag: 'div',
29564             cls: 'roo-location-picker'
29565         };
29566         
29567         return cfg
29568     },
29569     
29570     initEvents: function(ct, position)
29571     {       
29572         if(!this.el.getWidth() || this.isApplied()){
29573             return;
29574         }
29575         
29576         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29577         
29578         this.initial();
29579     },
29580     
29581     initial: function()
29582     {
29583         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29584             this.fireEvent('loadexception', this);
29585             return;
29586         }
29587         
29588         if(!this.mapTypeId){
29589             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29590         }
29591         
29592         this.gMapContext = this.GMapContext();
29593         
29594         this.initOverlayView();
29595         
29596         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29597         
29598         var _this = this;
29599                 
29600         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29601             _this.setPosition(_this.gMapContext.marker.position);
29602         });
29603         
29604         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29605             _this.fireEvent('mapClick', this, event);
29606             
29607         });
29608
29609         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29610             _this.fireEvent('mapRightClick', this, event);
29611             
29612         });
29613         
29614         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29615             _this.fireEvent('markerClick', this, event);
29616             
29617         });
29618
29619         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29620             _this.fireEvent('markerRightClick', this, event);
29621             
29622         });
29623         
29624         this.setPosition(this.gMapContext.location);
29625         
29626         this.fireEvent('initial', this, this.gMapContext.location);
29627     },
29628     
29629     initOverlayView: function()
29630     {
29631         var _this = this;
29632         
29633         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29634             
29635             draw: function()
29636             {
29637                 _this.fireEvent('OverlayViewDraw', _this);
29638             },
29639             
29640             onAdd: function()
29641             {
29642                 _this.fireEvent('OverlayViewOnAdd', _this);
29643             },
29644             
29645             onRemove: function()
29646             {
29647                 _this.fireEvent('OverlayViewOnRemove', _this);
29648             },
29649             
29650             show: function(cpx)
29651             {
29652                 _this.fireEvent('OverlayViewShow', _this, cpx);
29653             },
29654             
29655             hide: function()
29656             {
29657                 _this.fireEvent('OverlayViewHide', _this);
29658             }
29659             
29660         });
29661     },
29662     
29663     fromLatLngToContainerPixel: function(event)
29664     {
29665         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29666     },
29667     
29668     isApplied: function() 
29669     {
29670         return this.getGmapContext() == false ? false : true;
29671     },
29672     
29673     getGmapContext: function() 
29674     {
29675         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29676     },
29677     
29678     GMapContext: function() 
29679     {
29680         var position = new google.maps.LatLng(this.latitude, this.longitude);
29681         
29682         var _map = new google.maps.Map(this.el.dom, {
29683             center: position,
29684             zoom: this.zoom,
29685             mapTypeId: this.mapTypeId,
29686             mapTypeControl: this.mapTypeControl,
29687             disableDoubleClickZoom: this.disableDoubleClickZoom,
29688             scrollwheel: this.scrollwheel,
29689             streetViewControl: this.streetViewControl,
29690             locationName: this.locationName,
29691             draggable: this.draggable,
29692             enableAutocomplete: this.enableAutocomplete,
29693             enableReverseGeocode: this.enableReverseGeocode
29694         });
29695         
29696         var _marker = new google.maps.Marker({
29697             position: position,
29698             map: _map,
29699             title: this.markerTitle,
29700             draggable: this.draggable
29701         });
29702         
29703         return {
29704             map: _map,
29705             marker: _marker,
29706             circle: null,
29707             location: position,
29708             radius: this.radius,
29709             locationName: this.locationName,
29710             addressComponents: {
29711                 formatted_address: null,
29712                 addressLine1: null,
29713                 addressLine2: null,
29714                 streetName: null,
29715                 streetNumber: null,
29716                 city: null,
29717                 district: null,
29718                 state: null,
29719                 stateOrProvince: null
29720             },
29721             settings: this,
29722             domContainer: this.el.dom,
29723             geodecoder: new google.maps.Geocoder()
29724         };
29725     },
29726     
29727     drawCircle: function(center, radius, options) 
29728     {
29729         if (this.gMapContext.circle != null) {
29730             this.gMapContext.circle.setMap(null);
29731         }
29732         if (radius > 0) {
29733             radius *= 1;
29734             options = Roo.apply({}, options, {
29735                 strokeColor: "#0000FF",
29736                 strokeOpacity: .35,
29737                 strokeWeight: 2,
29738                 fillColor: "#0000FF",
29739                 fillOpacity: .2
29740             });
29741             
29742             options.map = this.gMapContext.map;
29743             options.radius = radius;
29744             options.center = center;
29745             this.gMapContext.circle = new google.maps.Circle(options);
29746             return this.gMapContext.circle;
29747         }
29748         
29749         return null;
29750     },
29751     
29752     setPosition: function(location) 
29753     {
29754         this.gMapContext.location = location;
29755         this.gMapContext.marker.setPosition(location);
29756         this.gMapContext.map.panTo(location);
29757         this.drawCircle(location, this.gMapContext.radius, {});
29758         
29759         var _this = this;
29760         
29761         if (this.gMapContext.settings.enableReverseGeocode) {
29762             this.gMapContext.geodecoder.geocode({
29763                 latLng: this.gMapContext.location
29764             }, function(results, status) {
29765                 
29766                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29767                     _this.gMapContext.locationName = results[0].formatted_address;
29768                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29769                     
29770                     _this.fireEvent('positionchanged', this, location);
29771                 }
29772             });
29773             
29774             return;
29775         }
29776         
29777         this.fireEvent('positionchanged', this, location);
29778     },
29779     
29780     resize: function()
29781     {
29782         google.maps.event.trigger(this.gMapContext.map, "resize");
29783         
29784         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29785         
29786         this.fireEvent('resize', this);
29787     },
29788     
29789     setPositionByLatLng: function(latitude, longitude)
29790     {
29791         this.setPosition(new google.maps.LatLng(latitude, longitude));
29792     },
29793     
29794     getCurrentPosition: function() 
29795     {
29796         return {
29797             latitude: this.gMapContext.location.lat(),
29798             longitude: this.gMapContext.location.lng()
29799         };
29800     },
29801     
29802     getAddressName: function() 
29803     {
29804         return this.gMapContext.locationName;
29805     },
29806     
29807     getAddressComponents: function() 
29808     {
29809         return this.gMapContext.addressComponents;
29810     },
29811     
29812     address_component_from_google_geocode: function(address_components) 
29813     {
29814         var result = {};
29815         
29816         for (var i = 0; i < address_components.length; i++) {
29817             var component = address_components[i];
29818             if (component.types.indexOf("postal_code") >= 0) {
29819                 result.postalCode = component.short_name;
29820             } else if (component.types.indexOf("street_number") >= 0) {
29821                 result.streetNumber = component.short_name;
29822             } else if (component.types.indexOf("route") >= 0) {
29823                 result.streetName = component.short_name;
29824             } else if (component.types.indexOf("neighborhood") >= 0) {
29825                 result.city = component.short_name;
29826             } else if (component.types.indexOf("locality") >= 0) {
29827                 result.city = component.short_name;
29828             } else if (component.types.indexOf("sublocality") >= 0) {
29829                 result.district = component.short_name;
29830             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29831                 result.stateOrProvince = component.short_name;
29832             } else if (component.types.indexOf("country") >= 0) {
29833                 result.country = component.short_name;
29834             }
29835         }
29836         
29837         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29838         result.addressLine2 = "";
29839         return result;
29840     },
29841     
29842     setZoomLevel: function(zoom)
29843     {
29844         this.gMapContext.map.setZoom(zoom);
29845     },
29846     
29847     show: function()
29848     {
29849         if(!this.el){
29850             return;
29851         }
29852         
29853         this.el.show();
29854         
29855         this.resize();
29856         
29857         this.fireEvent('show', this);
29858     },
29859     
29860     hide: function()
29861     {
29862         if(!this.el){
29863             return;
29864         }
29865         
29866         this.el.hide();
29867         
29868         this.fireEvent('hide', this);
29869     }
29870     
29871 });
29872
29873 Roo.apply(Roo.bootstrap.LocationPicker, {
29874     
29875     OverlayView : function(map, options)
29876     {
29877         options = options || {};
29878         
29879         this.setMap(map);
29880     }
29881     
29882     
29883 });/**
29884  * @class Roo.bootstrap.Alert
29885  * @extends Roo.bootstrap.Component
29886  * Bootstrap Alert class - shows an alert area box
29887  * eg
29888  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29889   Enter a valid email address
29890 </div>
29891  * @licence LGPL
29892  * @cfg {String} title The title of alert
29893  * @cfg {String} html The content of alert
29894  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29895  * @cfg {String} fa font-awesomeicon
29896  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29897  * @cfg {Boolean} close true to show a x closer
29898  * 
29899  * 
29900  * @constructor
29901  * Create a new alert
29902  * @param {Object} config The config object
29903  */
29904
29905
29906 Roo.bootstrap.Alert = function(config){
29907     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29908     
29909 };
29910
29911 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29912     
29913     title: '',
29914     html: '',
29915     weight: false,
29916     fa: false,
29917     faicon: false, // BC
29918     close : false,
29919     
29920     
29921     getAutoCreate : function()
29922     {
29923         
29924         var cfg = {
29925             tag : 'div',
29926             cls : 'alert',
29927             cn : [
29928                 {
29929                     tag: 'button',
29930                     type :  "button",
29931                     cls: "close",
29932                     html : '×',
29933                     style : this.close ? '' : 'display:none'
29934                 },
29935                 {
29936                     tag : 'i',
29937                     cls : 'roo-alert-icon'
29938                     
29939                 },
29940                 {
29941                     tag : 'b',
29942                     cls : 'roo-alert-title',
29943                     html : this.title
29944                 },
29945                 {
29946                     tag : 'span',
29947                     cls : 'roo-alert-text',
29948                     html : this.html
29949                 }
29950             ]
29951         };
29952         
29953         if(this.faicon){
29954             cfg.cn[0].cls += ' fa ' + this.faicon;
29955         }
29956         if(this.fa){
29957             cfg.cn[0].cls += ' fa ' + this.fa;
29958         }
29959         
29960         if(this.weight){
29961             cfg.cls += ' alert-' + this.weight;
29962         }
29963         
29964         return cfg;
29965     },
29966     
29967     initEvents: function() 
29968     {
29969         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29970         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29971         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29972         this.htmlEl = this.el.select('.roo-alert-text',true).first();
29973         if (this.seconds > 0) {
29974             this.hide.defer(this.seconds, this);
29975         }
29976     },
29977     /**
29978      * Set the Title Message HTML
29979      * @param {String} html
29980      */
29981     setTitle : function(str)
29982     {
29983         this.titleEl.dom.innerHTML = str;
29984     },
29985      
29986      /**
29987      * Set the Body Message HTML
29988      * @param {String} html
29989      */
29990     setHtml : function(str)
29991     {
29992         this.htmlEl.dom.innerHTML = str;
29993     },
29994     /**
29995      * Set the Weight of the alert
29996      * @param {String} (success|info|warning|danger) weight
29997      */
29998     
29999     setWeight : function(weight)
30000     {
30001         if(this.weight){
30002             this.el.removeClass('alert-' + this.weight);
30003         }
30004         
30005         this.weight = weight;
30006         
30007         this.el.addClass('alert-' + this.weight);
30008     },
30009       /**
30010      * Set the Icon of the alert
30011      * @param {String} see fontawsome names (name without the 'fa-' bit)
30012      */
30013     setIcon : function(icon)
30014     {
30015         if(this.faicon){
30016             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30017         }
30018         
30019         this.faicon = icon;
30020         
30021         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30022     },
30023     /**
30024      * Hide the Alert
30025      */
30026     hide: function() 
30027     {
30028         this.el.hide();   
30029     },
30030     /**
30031      * Show the Alert
30032      */
30033     show: function() 
30034     {  
30035         this.el.show();   
30036     }
30037     
30038 });
30039
30040  
30041 /*
30042 * Licence: LGPL
30043 */
30044
30045 /**
30046  * @class Roo.bootstrap.UploadCropbox
30047  * @extends Roo.bootstrap.Component
30048  * Bootstrap UploadCropbox class
30049  * @cfg {String} emptyText show when image has been loaded
30050  * @cfg {String} rotateNotify show when image too small to rotate
30051  * @cfg {Number} errorTimeout default 3000
30052  * @cfg {Number} minWidth default 300
30053  * @cfg {Number} minHeight default 300
30054  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30055  * @cfg {Boolean} isDocument (true|false) default false
30056  * @cfg {String} url action url
30057  * @cfg {String} paramName default 'imageUpload'
30058  * @cfg {String} method default POST
30059  * @cfg {Boolean} loadMask (true|false) default true
30060  * @cfg {Boolean} loadingText default 'Loading...'
30061  * 
30062  * @constructor
30063  * Create a new UploadCropbox
30064  * @param {Object} config The config object
30065  */
30066
30067 Roo.bootstrap.UploadCropbox = function(config){
30068     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30069     
30070     this.addEvents({
30071         /**
30072          * @event beforeselectfile
30073          * Fire before select file
30074          * @param {Roo.bootstrap.UploadCropbox} this
30075          */
30076         "beforeselectfile" : true,
30077         /**
30078          * @event initial
30079          * Fire after initEvent
30080          * @param {Roo.bootstrap.UploadCropbox} this
30081          */
30082         "initial" : true,
30083         /**
30084          * @event crop
30085          * Fire after initEvent
30086          * @param {Roo.bootstrap.UploadCropbox} this
30087          * @param {String} data
30088          */
30089         "crop" : true,
30090         /**
30091          * @event prepare
30092          * Fire when preparing the file data
30093          * @param {Roo.bootstrap.UploadCropbox} this
30094          * @param {Object} file
30095          */
30096         "prepare" : true,
30097         /**
30098          * @event exception
30099          * Fire when get exception
30100          * @param {Roo.bootstrap.UploadCropbox} this
30101          * @param {XMLHttpRequest} xhr
30102          */
30103         "exception" : true,
30104         /**
30105          * @event beforeloadcanvas
30106          * Fire before load the canvas
30107          * @param {Roo.bootstrap.UploadCropbox} this
30108          * @param {String} src
30109          */
30110         "beforeloadcanvas" : true,
30111         /**
30112          * @event trash
30113          * Fire when trash image
30114          * @param {Roo.bootstrap.UploadCropbox} this
30115          */
30116         "trash" : true,
30117         /**
30118          * @event download
30119          * Fire when download the image
30120          * @param {Roo.bootstrap.UploadCropbox} this
30121          */
30122         "download" : true,
30123         /**
30124          * @event footerbuttonclick
30125          * Fire when footerbuttonclick
30126          * @param {Roo.bootstrap.UploadCropbox} this
30127          * @param {String} type
30128          */
30129         "footerbuttonclick" : true,
30130         /**
30131          * @event resize
30132          * Fire when resize
30133          * @param {Roo.bootstrap.UploadCropbox} this
30134          */
30135         "resize" : true,
30136         /**
30137          * @event rotate
30138          * Fire when rotate the image
30139          * @param {Roo.bootstrap.UploadCropbox} this
30140          * @param {String} pos
30141          */
30142         "rotate" : true,
30143         /**
30144          * @event inspect
30145          * Fire when inspect the file
30146          * @param {Roo.bootstrap.UploadCropbox} this
30147          * @param {Object} file
30148          */
30149         "inspect" : true,
30150         /**
30151          * @event upload
30152          * Fire when xhr upload the file
30153          * @param {Roo.bootstrap.UploadCropbox} this
30154          * @param {Object} data
30155          */
30156         "upload" : true,
30157         /**
30158          * @event arrange
30159          * Fire when arrange the file data
30160          * @param {Roo.bootstrap.UploadCropbox} this
30161          * @param {Object} formData
30162          */
30163         "arrange" : true
30164     });
30165     
30166     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30167 };
30168
30169 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30170     
30171     emptyText : 'Click to upload image',
30172     rotateNotify : 'Image is too small to rotate',
30173     errorTimeout : 3000,
30174     scale : 0,
30175     baseScale : 1,
30176     rotate : 0,
30177     dragable : false,
30178     pinching : false,
30179     mouseX : 0,
30180     mouseY : 0,
30181     cropData : false,
30182     minWidth : 300,
30183     minHeight : 300,
30184     file : false,
30185     exif : {},
30186     baseRotate : 1,
30187     cropType : 'image/jpeg',
30188     buttons : false,
30189     canvasLoaded : false,
30190     isDocument : false,
30191     method : 'POST',
30192     paramName : 'imageUpload',
30193     loadMask : true,
30194     loadingText : 'Loading...',
30195     maskEl : false,
30196     
30197     getAutoCreate : function()
30198     {
30199         var cfg = {
30200             tag : 'div',
30201             cls : 'roo-upload-cropbox',
30202             cn : [
30203                 {
30204                     tag : 'input',
30205                     cls : 'roo-upload-cropbox-selector',
30206                     type : 'file'
30207                 },
30208                 {
30209                     tag : 'div',
30210                     cls : 'roo-upload-cropbox-body',
30211                     style : 'cursor:pointer',
30212                     cn : [
30213                         {
30214                             tag : 'div',
30215                             cls : 'roo-upload-cropbox-preview'
30216                         },
30217                         {
30218                             tag : 'div',
30219                             cls : 'roo-upload-cropbox-thumb'
30220                         },
30221                         {
30222                             tag : 'div',
30223                             cls : 'roo-upload-cropbox-empty-notify',
30224                             html : this.emptyText
30225                         },
30226                         {
30227                             tag : 'div',
30228                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30229                             html : this.rotateNotify
30230                         }
30231                     ]
30232                 },
30233                 {
30234                     tag : 'div',
30235                     cls : 'roo-upload-cropbox-footer',
30236                     cn : {
30237                         tag : 'div',
30238                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30239                         cn : []
30240                     }
30241                 }
30242             ]
30243         };
30244         
30245         return cfg;
30246     },
30247     
30248     onRender : function(ct, position)
30249     {
30250         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30251         
30252         if (this.buttons.length) {
30253             
30254             Roo.each(this.buttons, function(bb) {
30255                 
30256                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30257                 
30258                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30259                 
30260             }, this);
30261         }
30262         
30263         if(this.loadMask){
30264             this.maskEl = this.el;
30265         }
30266     },
30267     
30268     initEvents : function()
30269     {
30270         this.urlAPI = (window.createObjectURL && window) || 
30271                                 (window.URL && URL.revokeObjectURL && URL) || 
30272                                 (window.webkitURL && webkitURL);
30273                         
30274         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30275         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30276         
30277         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30278         this.selectorEl.hide();
30279         
30280         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30281         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30282         
30283         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30284         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30285         this.thumbEl.hide();
30286         
30287         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30288         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30289         
30290         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30291         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30292         this.errorEl.hide();
30293         
30294         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30295         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30296         this.footerEl.hide();
30297         
30298         this.setThumbBoxSize();
30299         
30300         this.bind();
30301         
30302         this.resize();
30303         
30304         this.fireEvent('initial', this);
30305     },
30306
30307     bind : function()
30308     {
30309         var _this = this;
30310         
30311         window.addEventListener("resize", function() { _this.resize(); } );
30312         
30313         this.bodyEl.on('click', this.beforeSelectFile, this);
30314         
30315         if(Roo.isTouch){
30316             this.bodyEl.on('touchstart', this.onTouchStart, this);
30317             this.bodyEl.on('touchmove', this.onTouchMove, this);
30318             this.bodyEl.on('touchend', this.onTouchEnd, this);
30319         }
30320         
30321         if(!Roo.isTouch){
30322             this.bodyEl.on('mousedown', this.onMouseDown, this);
30323             this.bodyEl.on('mousemove', this.onMouseMove, this);
30324             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30325             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30326             Roo.get(document).on('mouseup', this.onMouseUp, this);
30327         }
30328         
30329         this.selectorEl.on('change', this.onFileSelected, this);
30330     },
30331     
30332     reset : function()
30333     {    
30334         this.scale = 0;
30335         this.baseScale = 1;
30336         this.rotate = 0;
30337         this.baseRotate = 1;
30338         this.dragable = false;
30339         this.pinching = false;
30340         this.mouseX = 0;
30341         this.mouseY = 0;
30342         this.cropData = false;
30343         this.notifyEl.dom.innerHTML = this.emptyText;
30344         
30345         this.selectorEl.dom.value = '';
30346         
30347     },
30348     
30349     resize : function()
30350     {
30351         if(this.fireEvent('resize', this) != false){
30352             this.setThumbBoxPosition();
30353             this.setCanvasPosition();
30354         }
30355     },
30356     
30357     onFooterButtonClick : function(e, el, o, type)
30358     {
30359         switch (type) {
30360             case 'rotate-left' :
30361                 this.onRotateLeft(e);
30362                 break;
30363             case 'rotate-right' :
30364                 this.onRotateRight(e);
30365                 break;
30366             case 'picture' :
30367                 this.beforeSelectFile(e);
30368                 break;
30369             case 'trash' :
30370                 this.trash(e);
30371                 break;
30372             case 'crop' :
30373                 this.crop(e);
30374                 break;
30375             case 'download' :
30376                 this.download(e);
30377                 break;
30378             default :
30379                 break;
30380         }
30381         
30382         this.fireEvent('footerbuttonclick', this, type);
30383     },
30384     
30385     beforeSelectFile : function(e)
30386     {
30387         e.preventDefault();
30388         
30389         if(this.fireEvent('beforeselectfile', this) != false){
30390             this.selectorEl.dom.click();
30391         }
30392     },
30393     
30394     onFileSelected : function(e)
30395     {
30396         e.preventDefault();
30397         
30398         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30399             return;
30400         }
30401         
30402         var file = this.selectorEl.dom.files[0];
30403         
30404         if(this.fireEvent('inspect', this, file) != false){
30405             this.prepare(file);
30406         }
30407         
30408     },
30409     
30410     trash : function(e)
30411     {
30412         this.fireEvent('trash', this);
30413     },
30414     
30415     download : function(e)
30416     {
30417         this.fireEvent('download', this);
30418     },
30419     
30420     loadCanvas : function(src)
30421     {   
30422         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30423             
30424             this.reset();
30425             
30426             this.imageEl = document.createElement('img');
30427             
30428             var _this = this;
30429             
30430             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30431             
30432             this.imageEl.src = src;
30433         }
30434     },
30435     
30436     onLoadCanvas : function()
30437     {   
30438         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30439         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30440         
30441         this.bodyEl.un('click', this.beforeSelectFile, this);
30442         
30443         this.notifyEl.hide();
30444         this.thumbEl.show();
30445         this.footerEl.show();
30446         
30447         this.baseRotateLevel();
30448         
30449         if(this.isDocument){
30450             this.setThumbBoxSize();
30451         }
30452         
30453         this.setThumbBoxPosition();
30454         
30455         this.baseScaleLevel();
30456         
30457         this.draw();
30458         
30459         this.resize();
30460         
30461         this.canvasLoaded = true;
30462         
30463         if(this.loadMask){
30464             this.maskEl.unmask();
30465         }
30466         
30467     },
30468     
30469     setCanvasPosition : function()
30470     {   
30471         if(!this.canvasEl){
30472             return;
30473         }
30474         
30475         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30476         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30477         
30478         this.previewEl.setLeft(pw);
30479         this.previewEl.setTop(ph);
30480         
30481     },
30482     
30483     onMouseDown : function(e)
30484     {   
30485         e.stopEvent();
30486         
30487         this.dragable = true;
30488         this.pinching = false;
30489         
30490         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30491             this.dragable = false;
30492             return;
30493         }
30494         
30495         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30496         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30497         
30498     },
30499     
30500     onMouseMove : function(e)
30501     {   
30502         e.stopEvent();
30503         
30504         if(!this.canvasLoaded){
30505             return;
30506         }
30507         
30508         if (!this.dragable){
30509             return;
30510         }
30511         
30512         var minX = Math.ceil(this.thumbEl.getLeft(true));
30513         var minY = Math.ceil(this.thumbEl.getTop(true));
30514         
30515         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30516         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30517         
30518         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30519         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30520         
30521         x = x - this.mouseX;
30522         y = y - this.mouseY;
30523         
30524         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30525         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30526         
30527         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30528         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30529         
30530         this.previewEl.setLeft(bgX);
30531         this.previewEl.setTop(bgY);
30532         
30533         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30534         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30535     },
30536     
30537     onMouseUp : function(e)
30538     {   
30539         e.stopEvent();
30540         
30541         this.dragable = false;
30542     },
30543     
30544     onMouseWheel : function(e)
30545     {   
30546         e.stopEvent();
30547         
30548         this.startScale = this.scale;
30549         
30550         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30551         
30552         if(!this.zoomable()){
30553             this.scale = this.startScale;
30554             return;
30555         }
30556         
30557         this.draw();
30558         
30559         return;
30560     },
30561     
30562     zoomable : function()
30563     {
30564         var minScale = this.thumbEl.getWidth() / this.minWidth;
30565         
30566         if(this.minWidth < this.minHeight){
30567             minScale = this.thumbEl.getHeight() / this.minHeight;
30568         }
30569         
30570         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30571         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30572         
30573         if(
30574                 this.isDocument &&
30575                 (this.rotate == 0 || this.rotate == 180) && 
30576                 (
30577                     width > this.imageEl.OriginWidth || 
30578                     height > this.imageEl.OriginHeight ||
30579                     (width < this.minWidth && height < this.minHeight)
30580                 )
30581         ){
30582             return false;
30583         }
30584         
30585         if(
30586                 this.isDocument &&
30587                 (this.rotate == 90 || this.rotate == 270) && 
30588                 (
30589                     width > this.imageEl.OriginWidth || 
30590                     height > this.imageEl.OriginHeight ||
30591                     (width < this.minHeight && height < this.minWidth)
30592                 )
30593         ){
30594             return false;
30595         }
30596         
30597         if(
30598                 !this.isDocument &&
30599                 (this.rotate == 0 || this.rotate == 180) && 
30600                 (
30601                     width < this.minWidth || 
30602                     width > this.imageEl.OriginWidth || 
30603                     height < this.minHeight || 
30604                     height > this.imageEl.OriginHeight
30605                 )
30606         ){
30607             return false;
30608         }
30609         
30610         if(
30611                 !this.isDocument &&
30612                 (this.rotate == 90 || this.rotate == 270) && 
30613                 (
30614                     width < this.minHeight || 
30615                     width > this.imageEl.OriginWidth || 
30616                     height < this.minWidth || 
30617                     height > this.imageEl.OriginHeight
30618                 )
30619         ){
30620             return false;
30621         }
30622         
30623         return true;
30624         
30625     },
30626     
30627     onRotateLeft : function(e)
30628     {   
30629         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30630             
30631             var minScale = this.thumbEl.getWidth() / this.minWidth;
30632             
30633             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30634             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30635             
30636             this.startScale = this.scale;
30637             
30638             while (this.getScaleLevel() < minScale){
30639             
30640                 this.scale = this.scale + 1;
30641                 
30642                 if(!this.zoomable()){
30643                     break;
30644                 }
30645                 
30646                 if(
30647                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30648                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30649                 ){
30650                     continue;
30651                 }
30652                 
30653                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30654
30655                 this.draw();
30656                 
30657                 return;
30658             }
30659             
30660             this.scale = this.startScale;
30661             
30662             this.onRotateFail();
30663             
30664             return false;
30665         }
30666         
30667         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30668
30669         if(this.isDocument){
30670             this.setThumbBoxSize();
30671             this.setThumbBoxPosition();
30672             this.setCanvasPosition();
30673         }
30674         
30675         this.draw();
30676         
30677         this.fireEvent('rotate', this, 'left');
30678         
30679     },
30680     
30681     onRotateRight : function(e)
30682     {
30683         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30684             
30685             var minScale = this.thumbEl.getWidth() / this.minWidth;
30686         
30687             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30688             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30689             
30690             this.startScale = this.scale;
30691             
30692             while (this.getScaleLevel() < minScale){
30693             
30694                 this.scale = this.scale + 1;
30695                 
30696                 if(!this.zoomable()){
30697                     break;
30698                 }
30699                 
30700                 if(
30701                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30702                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30703                 ){
30704                     continue;
30705                 }
30706                 
30707                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30708
30709                 this.draw();
30710                 
30711                 return;
30712             }
30713             
30714             this.scale = this.startScale;
30715             
30716             this.onRotateFail();
30717             
30718             return false;
30719         }
30720         
30721         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30722
30723         if(this.isDocument){
30724             this.setThumbBoxSize();
30725             this.setThumbBoxPosition();
30726             this.setCanvasPosition();
30727         }
30728         
30729         this.draw();
30730         
30731         this.fireEvent('rotate', this, 'right');
30732     },
30733     
30734     onRotateFail : function()
30735     {
30736         this.errorEl.show(true);
30737         
30738         var _this = this;
30739         
30740         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30741     },
30742     
30743     draw : function()
30744     {
30745         this.previewEl.dom.innerHTML = '';
30746         
30747         var canvasEl = document.createElement("canvas");
30748         
30749         var contextEl = canvasEl.getContext("2d");
30750         
30751         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30752         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30753         var center = this.imageEl.OriginWidth / 2;
30754         
30755         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30756             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30758             center = this.imageEl.OriginHeight / 2;
30759         }
30760         
30761         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30762         
30763         contextEl.translate(center, center);
30764         contextEl.rotate(this.rotate * Math.PI / 180);
30765
30766         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30767         
30768         this.canvasEl = document.createElement("canvas");
30769         
30770         this.contextEl = this.canvasEl.getContext("2d");
30771         
30772         switch (this.rotate) {
30773             case 0 :
30774                 
30775                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30776                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30777                 
30778                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30779                 
30780                 break;
30781             case 90 : 
30782                 
30783                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30784                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30785                 
30786                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30787                     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);
30788                     break;
30789                 }
30790                 
30791                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30792                 
30793                 break;
30794             case 180 :
30795                 
30796                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30797                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30798                 
30799                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30800                     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);
30801                     break;
30802                 }
30803                 
30804                 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);
30805                 
30806                 break;
30807             case 270 :
30808                 
30809                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30810                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30811         
30812                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30813                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30814                     break;
30815                 }
30816                 
30817                 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);
30818                 
30819                 break;
30820             default : 
30821                 break;
30822         }
30823         
30824         this.previewEl.appendChild(this.canvasEl);
30825         
30826         this.setCanvasPosition();
30827     },
30828     
30829     crop : function()
30830     {
30831         if(!this.canvasLoaded){
30832             return;
30833         }
30834         
30835         var imageCanvas = document.createElement("canvas");
30836         
30837         var imageContext = imageCanvas.getContext("2d");
30838         
30839         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30840         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30841         
30842         var center = imageCanvas.width / 2;
30843         
30844         imageContext.translate(center, center);
30845         
30846         imageContext.rotate(this.rotate * Math.PI / 180);
30847         
30848         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30849         
30850         var canvas = document.createElement("canvas");
30851         
30852         var context = canvas.getContext("2d");
30853                 
30854         canvas.width = this.minWidth;
30855         canvas.height = this.minHeight;
30856
30857         switch (this.rotate) {
30858             case 0 :
30859                 
30860                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30861                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30862                 
30863                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30864                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30865                 
30866                 var targetWidth = this.minWidth - 2 * x;
30867                 var targetHeight = this.minHeight - 2 * y;
30868                 
30869                 var scale = 1;
30870                 
30871                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30872                     scale = targetWidth / width;
30873                 }
30874                 
30875                 if(x > 0 && y == 0){
30876                     scale = targetHeight / height;
30877                 }
30878                 
30879                 if(x > 0 && y > 0){
30880                     scale = targetWidth / width;
30881                     
30882                     if(width < height){
30883                         scale = targetHeight / height;
30884                     }
30885                 }
30886                 
30887                 context.scale(scale, scale);
30888                 
30889                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30890                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30891
30892                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30893                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30894
30895                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30896                 
30897                 break;
30898             case 90 : 
30899                 
30900                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30901                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30902                 
30903                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30904                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30905                 
30906                 var targetWidth = this.minWidth - 2 * x;
30907                 var targetHeight = this.minHeight - 2 * y;
30908                 
30909                 var scale = 1;
30910                 
30911                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30912                     scale = targetWidth / width;
30913                 }
30914                 
30915                 if(x > 0 && y == 0){
30916                     scale = targetHeight / height;
30917                 }
30918                 
30919                 if(x > 0 && y > 0){
30920                     scale = targetWidth / width;
30921                     
30922                     if(width < height){
30923                         scale = targetHeight / height;
30924                     }
30925                 }
30926                 
30927                 context.scale(scale, scale);
30928                 
30929                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30930                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30931
30932                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30933                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30934                 
30935                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30936                 
30937                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30938                 
30939                 break;
30940             case 180 :
30941                 
30942                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30943                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30944                 
30945                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30946                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30947                 
30948                 var targetWidth = this.minWidth - 2 * x;
30949                 var targetHeight = this.minHeight - 2 * y;
30950                 
30951                 var scale = 1;
30952                 
30953                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30954                     scale = targetWidth / width;
30955                 }
30956                 
30957                 if(x > 0 && y == 0){
30958                     scale = targetHeight / height;
30959                 }
30960                 
30961                 if(x > 0 && y > 0){
30962                     scale = targetWidth / width;
30963                     
30964                     if(width < height){
30965                         scale = targetHeight / height;
30966                     }
30967                 }
30968                 
30969                 context.scale(scale, scale);
30970                 
30971                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30972                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30973
30974                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30975                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30976
30977                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30978                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30979                 
30980                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30981                 
30982                 break;
30983             case 270 :
30984                 
30985                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30986                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30987                 
30988                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30989                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30990                 
30991                 var targetWidth = this.minWidth - 2 * x;
30992                 var targetHeight = this.minHeight - 2 * y;
30993                 
30994                 var scale = 1;
30995                 
30996                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30997                     scale = targetWidth / width;
30998                 }
30999                 
31000                 if(x > 0 && y == 0){
31001                     scale = targetHeight / height;
31002                 }
31003                 
31004                 if(x > 0 && y > 0){
31005                     scale = targetWidth / width;
31006                     
31007                     if(width < height){
31008                         scale = targetHeight / height;
31009                     }
31010                 }
31011                 
31012                 context.scale(scale, scale);
31013                 
31014                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31015                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31016
31017                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31018                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31019                 
31020                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31021                 
31022                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31023                 
31024                 break;
31025             default : 
31026                 break;
31027         }
31028         
31029         this.cropData = canvas.toDataURL(this.cropType);
31030         
31031         if(this.fireEvent('crop', this, this.cropData) !== false){
31032             this.process(this.file, this.cropData);
31033         }
31034         
31035         return;
31036         
31037     },
31038     
31039     setThumbBoxSize : function()
31040     {
31041         var width, height;
31042         
31043         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31044             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31045             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31046             
31047             this.minWidth = width;
31048             this.minHeight = height;
31049             
31050             if(this.rotate == 90 || this.rotate == 270){
31051                 this.minWidth = height;
31052                 this.minHeight = width;
31053             }
31054         }
31055         
31056         height = 300;
31057         width = Math.ceil(this.minWidth * height / this.minHeight);
31058         
31059         if(this.minWidth > this.minHeight){
31060             width = 300;
31061             height = Math.ceil(this.minHeight * width / this.minWidth);
31062         }
31063         
31064         this.thumbEl.setStyle({
31065             width : width + 'px',
31066             height : height + 'px'
31067         });
31068
31069         return;
31070             
31071     },
31072     
31073     setThumbBoxPosition : function()
31074     {
31075         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31076         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31077         
31078         this.thumbEl.setLeft(x);
31079         this.thumbEl.setTop(y);
31080         
31081     },
31082     
31083     baseRotateLevel : function()
31084     {
31085         this.baseRotate = 1;
31086         
31087         if(
31088                 typeof(this.exif) != 'undefined' &&
31089                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31090                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31091         ){
31092             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31093         }
31094         
31095         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31096         
31097     },
31098     
31099     baseScaleLevel : function()
31100     {
31101         var width, height;
31102         
31103         if(this.isDocument){
31104             
31105             if(this.baseRotate == 6 || this.baseRotate == 8){
31106             
31107                 height = this.thumbEl.getHeight();
31108                 this.baseScale = height / this.imageEl.OriginWidth;
31109
31110                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31111                     width = this.thumbEl.getWidth();
31112                     this.baseScale = width / this.imageEl.OriginHeight;
31113                 }
31114
31115                 return;
31116             }
31117
31118             height = this.thumbEl.getHeight();
31119             this.baseScale = height / this.imageEl.OriginHeight;
31120
31121             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31122                 width = this.thumbEl.getWidth();
31123                 this.baseScale = width / this.imageEl.OriginWidth;
31124             }
31125
31126             return;
31127         }
31128         
31129         if(this.baseRotate == 6 || this.baseRotate == 8){
31130             
31131             width = this.thumbEl.getHeight();
31132             this.baseScale = width / this.imageEl.OriginHeight;
31133             
31134             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31135                 height = this.thumbEl.getWidth();
31136                 this.baseScale = height / this.imageEl.OriginHeight;
31137             }
31138             
31139             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31140                 height = this.thumbEl.getWidth();
31141                 this.baseScale = height / this.imageEl.OriginHeight;
31142                 
31143                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31144                     width = this.thumbEl.getHeight();
31145                     this.baseScale = width / this.imageEl.OriginWidth;
31146                 }
31147             }
31148             
31149             return;
31150         }
31151         
31152         width = this.thumbEl.getWidth();
31153         this.baseScale = width / this.imageEl.OriginWidth;
31154         
31155         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31156             height = this.thumbEl.getHeight();
31157             this.baseScale = height / this.imageEl.OriginHeight;
31158         }
31159         
31160         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31161             
31162             height = this.thumbEl.getHeight();
31163             this.baseScale = height / this.imageEl.OriginHeight;
31164             
31165             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31166                 width = this.thumbEl.getWidth();
31167                 this.baseScale = width / this.imageEl.OriginWidth;
31168             }
31169             
31170         }
31171         
31172         return;
31173     },
31174     
31175     getScaleLevel : function()
31176     {
31177         return this.baseScale * Math.pow(1.1, this.scale);
31178     },
31179     
31180     onTouchStart : function(e)
31181     {
31182         if(!this.canvasLoaded){
31183             this.beforeSelectFile(e);
31184             return;
31185         }
31186         
31187         var touches = e.browserEvent.touches;
31188         
31189         if(!touches){
31190             return;
31191         }
31192         
31193         if(touches.length == 1){
31194             this.onMouseDown(e);
31195             return;
31196         }
31197         
31198         if(touches.length != 2){
31199             return;
31200         }
31201         
31202         var coords = [];
31203         
31204         for(var i = 0, finger; finger = touches[i]; i++){
31205             coords.push(finger.pageX, finger.pageY);
31206         }
31207         
31208         var x = Math.pow(coords[0] - coords[2], 2);
31209         var y = Math.pow(coords[1] - coords[3], 2);
31210         
31211         this.startDistance = Math.sqrt(x + y);
31212         
31213         this.startScale = this.scale;
31214         
31215         this.pinching = true;
31216         this.dragable = false;
31217         
31218     },
31219     
31220     onTouchMove : function(e)
31221     {
31222         if(!this.pinching && !this.dragable){
31223             return;
31224         }
31225         
31226         var touches = e.browserEvent.touches;
31227         
31228         if(!touches){
31229             return;
31230         }
31231         
31232         if(this.dragable){
31233             this.onMouseMove(e);
31234             return;
31235         }
31236         
31237         var coords = [];
31238         
31239         for(var i = 0, finger; finger = touches[i]; i++){
31240             coords.push(finger.pageX, finger.pageY);
31241         }
31242         
31243         var x = Math.pow(coords[0] - coords[2], 2);
31244         var y = Math.pow(coords[1] - coords[3], 2);
31245         
31246         this.endDistance = Math.sqrt(x + y);
31247         
31248         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31249         
31250         if(!this.zoomable()){
31251             this.scale = this.startScale;
31252             return;
31253         }
31254         
31255         this.draw();
31256         
31257     },
31258     
31259     onTouchEnd : function(e)
31260     {
31261         this.pinching = false;
31262         this.dragable = false;
31263         
31264     },
31265     
31266     process : function(file, crop)
31267     {
31268         if(this.loadMask){
31269             this.maskEl.mask(this.loadingText);
31270         }
31271         
31272         this.xhr = new XMLHttpRequest();
31273         
31274         file.xhr = this.xhr;
31275
31276         this.xhr.open(this.method, this.url, true);
31277         
31278         var headers = {
31279             "Accept": "application/json",
31280             "Cache-Control": "no-cache",
31281             "X-Requested-With": "XMLHttpRequest"
31282         };
31283         
31284         for (var headerName in headers) {
31285             var headerValue = headers[headerName];
31286             if (headerValue) {
31287                 this.xhr.setRequestHeader(headerName, headerValue);
31288             }
31289         }
31290         
31291         var _this = this;
31292         
31293         this.xhr.onload = function()
31294         {
31295             _this.xhrOnLoad(_this.xhr);
31296         }
31297         
31298         this.xhr.onerror = function()
31299         {
31300             _this.xhrOnError(_this.xhr);
31301         }
31302         
31303         var formData = new FormData();
31304
31305         formData.append('returnHTML', 'NO');
31306         
31307         if(crop){
31308             formData.append('crop', crop);
31309         }
31310         
31311         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31312             formData.append(this.paramName, file, file.name);
31313         }
31314         
31315         if(typeof(file.filename) != 'undefined'){
31316             formData.append('filename', file.filename);
31317         }
31318         
31319         if(typeof(file.mimetype) != 'undefined'){
31320             formData.append('mimetype', file.mimetype);
31321         }
31322         
31323         if(this.fireEvent('arrange', this, formData) != false){
31324             this.xhr.send(formData);
31325         };
31326     },
31327     
31328     xhrOnLoad : function(xhr)
31329     {
31330         if(this.loadMask){
31331             this.maskEl.unmask();
31332         }
31333         
31334         if (xhr.readyState !== 4) {
31335             this.fireEvent('exception', this, xhr);
31336             return;
31337         }
31338
31339         var response = Roo.decode(xhr.responseText);
31340         
31341         if(!response.success){
31342             this.fireEvent('exception', this, xhr);
31343             return;
31344         }
31345         
31346         var response = Roo.decode(xhr.responseText);
31347         
31348         this.fireEvent('upload', this, response);
31349         
31350     },
31351     
31352     xhrOnError : function()
31353     {
31354         if(this.loadMask){
31355             this.maskEl.unmask();
31356         }
31357         
31358         Roo.log('xhr on error');
31359         
31360         var response = Roo.decode(xhr.responseText);
31361           
31362         Roo.log(response);
31363         
31364     },
31365     
31366     prepare : function(file)
31367     {   
31368         if(this.loadMask){
31369             this.maskEl.mask(this.loadingText);
31370         }
31371         
31372         this.file = false;
31373         this.exif = {};
31374         
31375         if(typeof(file) === 'string'){
31376             this.loadCanvas(file);
31377             return;
31378         }
31379         
31380         if(!file || !this.urlAPI){
31381             return;
31382         }
31383         
31384         this.file = file;
31385         this.cropType = file.type;
31386         
31387         var _this = this;
31388         
31389         if(this.fireEvent('prepare', this, this.file) != false){
31390             
31391             var reader = new FileReader();
31392             
31393             reader.onload = function (e) {
31394                 if (e.target.error) {
31395                     Roo.log(e.target.error);
31396                     return;
31397                 }
31398                 
31399                 var buffer = e.target.result,
31400                     dataView = new DataView(buffer),
31401                     offset = 2,
31402                     maxOffset = dataView.byteLength - 4,
31403                     markerBytes,
31404                     markerLength;
31405                 
31406                 if (dataView.getUint16(0) === 0xffd8) {
31407                     while (offset < maxOffset) {
31408                         markerBytes = dataView.getUint16(offset);
31409                         
31410                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31411                             markerLength = dataView.getUint16(offset + 2) + 2;
31412                             if (offset + markerLength > dataView.byteLength) {
31413                                 Roo.log('Invalid meta data: Invalid segment size.');
31414                                 break;
31415                             }
31416                             
31417                             if(markerBytes == 0xffe1){
31418                                 _this.parseExifData(
31419                                     dataView,
31420                                     offset,
31421                                     markerLength
31422                                 );
31423                             }
31424                             
31425                             offset += markerLength;
31426                             
31427                             continue;
31428                         }
31429                         
31430                         break;
31431                     }
31432                     
31433                 }
31434                 
31435                 var url = _this.urlAPI.createObjectURL(_this.file);
31436                 
31437                 _this.loadCanvas(url);
31438                 
31439                 return;
31440             }
31441             
31442             reader.readAsArrayBuffer(this.file);
31443             
31444         }
31445         
31446     },
31447     
31448     parseExifData : function(dataView, offset, length)
31449     {
31450         var tiffOffset = offset + 10,
31451             littleEndian,
31452             dirOffset;
31453     
31454         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31455             // No Exif data, might be XMP data instead
31456             return;
31457         }
31458         
31459         // Check for the ASCII code for "Exif" (0x45786966):
31460         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31461             // No Exif data, might be XMP data instead
31462             return;
31463         }
31464         if (tiffOffset + 8 > dataView.byteLength) {
31465             Roo.log('Invalid Exif data: Invalid segment size.');
31466             return;
31467         }
31468         // Check for the two null bytes:
31469         if (dataView.getUint16(offset + 8) !== 0x0000) {
31470             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31471             return;
31472         }
31473         // Check the byte alignment:
31474         switch (dataView.getUint16(tiffOffset)) {
31475         case 0x4949:
31476             littleEndian = true;
31477             break;
31478         case 0x4D4D:
31479             littleEndian = false;
31480             break;
31481         default:
31482             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31483             return;
31484         }
31485         // Check for the TIFF tag marker (0x002A):
31486         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31487             Roo.log('Invalid Exif data: Missing TIFF marker.');
31488             return;
31489         }
31490         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31491         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31492         
31493         this.parseExifTags(
31494             dataView,
31495             tiffOffset,
31496             tiffOffset + dirOffset,
31497             littleEndian
31498         );
31499     },
31500     
31501     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31502     {
31503         var tagsNumber,
31504             dirEndOffset,
31505             i;
31506         if (dirOffset + 6 > dataView.byteLength) {
31507             Roo.log('Invalid Exif data: Invalid directory offset.');
31508             return;
31509         }
31510         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31511         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31512         if (dirEndOffset + 4 > dataView.byteLength) {
31513             Roo.log('Invalid Exif data: Invalid directory size.');
31514             return;
31515         }
31516         for (i = 0; i < tagsNumber; i += 1) {
31517             this.parseExifTag(
31518                 dataView,
31519                 tiffOffset,
31520                 dirOffset + 2 + 12 * i, // tag offset
31521                 littleEndian
31522             );
31523         }
31524         // Return the offset to the next directory:
31525         return dataView.getUint32(dirEndOffset, littleEndian);
31526     },
31527     
31528     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31529     {
31530         var tag = dataView.getUint16(offset, littleEndian);
31531         
31532         this.exif[tag] = this.getExifValue(
31533             dataView,
31534             tiffOffset,
31535             offset,
31536             dataView.getUint16(offset + 2, littleEndian), // tag type
31537             dataView.getUint32(offset + 4, littleEndian), // tag length
31538             littleEndian
31539         );
31540     },
31541     
31542     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31543     {
31544         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31545             tagSize,
31546             dataOffset,
31547             values,
31548             i,
31549             str,
31550             c;
31551     
31552         if (!tagType) {
31553             Roo.log('Invalid Exif data: Invalid tag type.');
31554             return;
31555         }
31556         
31557         tagSize = tagType.size * length;
31558         // Determine if the value is contained in the dataOffset bytes,
31559         // or if the value at the dataOffset is a pointer to the actual data:
31560         dataOffset = tagSize > 4 ?
31561                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31562         if (dataOffset + tagSize > dataView.byteLength) {
31563             Roo.log('Invalid Exif data: Invalid data offset.');
31564             return;
31565         }
31566         if (length === 1) {
31567             return tagType.getValue(dataView, dataOffset, littleEndian);
31568         }
31569         values = [];
31570         for (i = 0; i < length; i += 1) {
31571             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31572         }
31573         
31574         if (tagType.ascii) {
31575             str = '';
31576             // Concatenate the chars:
31577             for (i = 0; i < values.length; i += 1) {
31578                 c = values[i];
31579                 // Ignore the terminating NULL byte(s):
31580                 if (c === '\u0000') {
31581                     break;
31582                 }
31583                 str += c;
31584             }
31585             return str;
31586         }
31587         return values;
31588     }
31589     
31590 });
31591
31592 Roo.apply(Roo.bootstrap.UploadCropbox, {
31593     tags : {
31594         'Orientation': 0x0112
31595     },
31596     
31597     Orientation: {
31598             1: 0, //'top-left',
31599 //            2: 'top-right',
31600             3: 180, //'bottom-right',
31601 //            4: 'bottom-left',
31602 //            5: 'left-top',
31603             6: 90, //'right-top',
31604 //            7: 'right-bottom',
31605             8: 270 //'left-bottom'
31606     },
31607     
31608     exifTagTypes : {
31609         // byte, 8-bit unsigned int:
31610         1: {
31611             getValue: function (dataView, dataOffset) {
31612                 return dataView.getUint8(dataOffset);
31613             },
31614             size: 1
31615         },
31616         // ascii, 8-bit byte:
31617         2: {
31618             getValue: function (dataView, dataOffset) {
31619                 return String.fromCharCode(dataView.getUint8(dataOffset));
31620             },
31621             size: 1,
31622             ascii: true
31623         },
31624         // short, 16 bit int:
31625         3: {
31626             getValue: function (dataView, dataOffset, littleEndian) {
31627                 return dataView.getUint16(dataOffset, littleEndian);
31628             },
31629             size: 2
31630         },
31631         // long, 32 bit int:
31632         4: {
31633             getValue: function (dataView, dataOffset, littleEndian) {
31634                 return dataView.getUint32(dataOffset, littleEndian);
31635             },
31636             size: 4
31637         },
31638         // rational = two long values, first is numerator, second is denominator:
31639         5: {
31640             getValue: function (dataView, dataOffset, littleEndian) {
31641                 return dataView.getUint32(dataOffset, littleEndian) /
31642                     dataView.getUint32(dataOffset + 4, littleEndian);
31643             },
31644             size: 8
31645         },
31646         // slong, 32 bit signed int:
31647         9: {
31648             getValue: function (dataView, dataOffset, littleEndian) {
31649                 return dataView.getInt32(dataOffset, littleEndian);
31650             },
31651             size: 4
31652         },
31653         // srational, two slongs, first is numerator, second is denominator:
31654         10: {
31655             getValue: function (dataView, dataOffset, littleEndian) {
31656                 return dataView.getInt32(dataOffset, littleEndian) /
31657                     dataView.getInt32(dataOffset + 4, littleEndian);
31658             },
31659             size: 8
31660         }
31661     },
31662     
31663     footer : {
31664         STANDARD : [
31665             {
31666                 tag : 'div',
31667                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31668                 action : 'rotate-left',
31669                 cn : [
31670                     {
31671                         tag : 'button',
31672                         cls : 'btn btn-default',
31673                         html : '<i class="fa fa-undo"></i>'
31674                     }
31675                 ]
31676             },
31677             {
31678                 tag : 'div',
31679                 cls : 'btn-group roo-upload-cropbox-picture',
31680                 action : 'picture',
31681                 cn : [
31682                     {
31683                         tag : 'button',
31684                         cls : 'btn btn-default',
31685                         html : '<i class="fa fa-picture-o"></i>'
31686                     }
31687                 ]
31688             },
31689             {
31690                 tag : 'div',
31691                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31692                 action : 'rotate-right',
31693                 cn : [
31694                     {
31695                         tag : 'button',
31696                         cls : 'btn btn-default',
31697                         html : '<i class="fa fa-repeat"></i>'
31698                     }
31699                 ]
31700             }
31701         ],
31702         DOCUMENT : [
31703             {
31704                 tag : 'div',
31705                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31706                 action : 'rotate-left',
31707                 cn : [
31708                     {
31709                         tag : 'button',
31710                         cls : 'btn btn-default',
31711                         html : '<i class="fa fa-undo"></i>'
31712                     }
31713                 ]
31714             },
31715             {
31716                 tag : 'div',
31717                 cls : 'btn-group roo-upload-cropbox-download',
31718                 action : 'download',
31719                 cn : [
31720                     {
31721                         tag : 'button',
31722                         cls : 'btn btn-default',
31723                         html : '<i class="fa fa-download"></i>'
31724                     }
31725                 ]
31726             },
31727             {
31728                 tag : 'div',
31729                 cls : 'btn-group roo-upload-cropbox-crop',
31730                 action : 'crop',
31731                 cn : [
31732                     {
31733                         tag : 'button',
31734                         cls : 'btn btn-default',
31735                         html : '<i class="fa fa-crop"></i>'
31736                     }
31737                 ]
31738             },
31739             {
31740                 tag : 'div',
31741                 cls : 'btn-group roo-upload-cropbox-trash',
31742                 action : 'trash',
31743                 cn : [
31744                     {
31745                         tag : 'button',
31746                         cls : 'btn btn-default',
31747                         html : '<i class="fa fa-trash"></i>'
31748                     }
31749                 ]
31750             },
31751             {
31752                 tag : 'div',
31753                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31754                 action : 'rotate-right',
31755                 cn : [
31756                     {
31757                         tag : 'button',
31758                         cls : 'btn btn-default',
31759                         html : '<i class="fa fa-repeat"></i>'
31760                     }
31761                 ]
31762             }
31763         ],
31764         ROTATOR : [
31765             {
31766                 tag : 'div',
31767                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31768                 action : 'rotate-left',
31769                 cn : [
31770                     {
31771                         tag : 'button',
31772                         cls : 'btn btn-default',
31773                         html : '<i class="fa fa-undo"></i>'
31774                     }
31775                 ]
31776             },
31777             {
31778                 tag : 'div',
31779                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31780                 action : 'rotate-right',
31781                 cn : [
31782                     {
31783                         tag : 'button',
31784                         cls : 'btn btn-default',
31785                         html : '<i class="fa fa-repeat"></i>'
31786                     }
31787                 ]
31788             }
31789         ]
31790     }
31791 });
31792
31793 /*
31794 * Licence: LGPL
31795 */
31796
31797 /**
31798  * @class Roo.bootstrap.DocumentManager
31799  * @extends Roo.bootstrap.Component
31800  * Bootstrap DocumentManager class
31801  * @cfg {String} paramName default 'imageUpload'
31802  * @cfg {String} toolTipName default 'filename'
31803  * @cfg {String} method default POST
31804  * @cfg {String} url action url
31805  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31806  * @cfg {Boolean} multiple multiple upload default true
31807  * @cfg {Number} thumbSize default 300
31808  * @cfg {String} fieldLabel
31809  * @cfg {Number} labelWidth default 4
31810  * @cfg {String} labelAlign (left|top) default left
31811  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31812 * @cfg {Number} labellg set the width of label (1-12)
31813  * @cfg {Number} labelmd set the width of label (1-12)
31814  * @cfg {Number} labelsm set the width of label (1-12)
31815  * @cfg {Number} labelxs set the width of label (1-12)
31816  * 
31817  * @constructor
31818  * Create a new DocumentManager
31819  * @param {Object} config The config object
31820  */
31821
31822 Roo.bootstrap.DocumentManager = function(config){
31823     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31824     
31825     this.files = [];
31826     this.delegates = [];
31827     
31828     this.addEvents({
31829         /**
31830          * @event initial
31831          * Fire when initial the DocumentManager
31832          * @param {Roo.bootstrap.DocumentManager} this
31833          */
31834         "initial" : true,
31835         /**
31836          * @event inspect
31837          * inspect selected file
31838          * @param {Roo.bootstrap.DocumentManager} this
31839          * @param {File} file
31840          */
31841         "inspect" : true,
31842         /**
31843          * @event exception
31844          * Fire when xhr load exception
31845          * @param {Roo.bootstrap.DocumentManager} this
31846          * @param {XMLHttpRequest} xhr
31847          */
31848         "exception" : true,
31849         /**
31850          * @event afterupload
31851          * Fire when xhr load exception
31852          * @param {Roo.bootstrap.DocumentManager} this
31853          * @param {XMLHttpRequest} xhr
31854          */
31855         "afterupload" : true,
31856         /**
31857          * @event prepare
31858          * prepare the form data
31859          * @param {Roo.bootstrap.DocumentManager} this
31860          * @param {Object} formData
31861          */
31862         "prepare" : true,
31863         /**
31864          * @event remove
31865          * Fire when remove the file
31866          * @param {Roo.bootstrap.DocumentManager} this
31867          * @param {Object} file
31868          */
31869         "remove" : true,
31870         /**
31871          * @event refresh
31872          * Fire after refresh the file
31873          * @param {Roo.bootstrap.DocumentManager} this
31874          */
31875         "refresh" : true,
31876         /**
31877          * @event click
31878          * Fire after click the image
31879          * @param {Roo.bootstrap.DocumentManager} this
31880          * @param {Object} file
31881          */
31882         "click" : true,
31883         /**
31884          * @event edit
31885          * Fire when upload a image and editable set to true
31886          * @param {Roo.bootstrap.DocumentManager} this
31887          * @param {Object} file
31888          */
31889         "edit" : true,
31890         /**
31891          * @event beforeselectfile
31892          * Fire before select file
31893          * @param {Roo.bootstrap.DocumentManager} this
31894          */
31895         "beforeselectfile" : true,
31896         /**
31897          * @event process
31898          * Fire before process file
31899          * @param {Roo.bootstrap.DocumentManager} this
31900          * @param {Object} file
31901          */
31902         "process" : true,
31903         /**
31904          * @event previewrendered
31905          * Fire when preview rendered
31906          * @param {Roo.bootstrap.DocumentManager} this
31907          * @param {Object} file
31908          */
31909         "previewrendered" : true,
31910         /**
31911          */
31912         "previewResize" : true
31913         
31914     });
31915 };
31916
31917 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31918     
31919     boxes : 0,
31920     inputName : '',
31921     thumbSize : 300,
31922     multiple : true,
31923     files : false,
31924     method : 'POST',
31925     url : '',
31926     paramName : 'imageUpload',
31927     toolTipName : 'filename',
31928     fieldLabel : '',
31929     labelWidth : 4,
31930     labelAlign : 'left',
31931     editable : true,
31932     delegates : false,
31933     xhr : false, 
31934     
31935     labellg : 0,
31936     labelmd : 0,
31937     labelsm : 0,
31938     labelxs : 0,
31939     
31940     getAutoCreate : function()
31941     {   
31942         var managerWidget = {
31943             tag : 'div',
31944             cls : 'roo-document-manager',
31945             cn : [
31946                 {
31947                     tag : 'input',
31948                     cls : 'roo-document-manager-selector',
31949                     type : 'file'
31950                 },
31951                 {
31952                     tag : 'div',
31953                     cls : 'roo-document-manager-uploader',
31954                     cn : [
31955                         {
31956                             tag : 'div',
31957                             cls : 'roo-document-manager-upload-btn',
31958                             html : '<i class="fa fa-plus"></i>'
31959                         }
31960                     ]
31961                     
31962                 }
31963             ]
31964         };
31965         
31966         var content = [
31967             {
31968                 tag : 'div',
31969                 cls : 'column col-md-12',
31970                 cn : managerWidget
31971             }
31972         ];
31973         
31974         if(this.fieldLabel.length){
31975             
31976             content = [
31977                 {
31978                     tag : 'div',
31979                     cls : 'column col-md-12',
31980                     html : this.fieldLabel
31981                 },
31982                 {
31983                     tag : 'div',
31984                     cls : 'column col-md-12',
31985                     cn : managerWidget
31986                 }
31987             ];
31988
31989             if(this.labelAlign == 'left'){
31990                 content = [
31991                     {
31992                         tag : 'div',
31993                         cls : 'column',
31994                         html : this.fieldLabel
31995                     },
31996                     {
31997                         tag : 'div',
31998                         cls : 'column',
31999                         cn : managerWidget
32000                     }
32001                 ];
32002                 
32003                 if(this.labelWidth > 12){
32004                     content[0].style = "width: " + this.labelWidth + 'px';
32005                 }
32006
32007                 if(this.labelWidth < 13 && this.labelmd == 0){
32008                     this.labelmd = this.labelWidth;
32009                 }
32010
32011                 if(this.labellg > 0){
32012                     content[0].cls += ' col-lg-' + this.labellg;
32013                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32014                 }
32015
32016                 if(this.labelmd > 0){
32017                     content[0].cls += ' col-md-' + this.labelmd;
32018                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32019                 }
32020
32021                 if(this.labelsm > 0){
32022                     content[0].cls += ' col-sm-' + this.labelsm;
32023                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32024                 }
32025
32026                 if(this.labelxs > 0){
32027                     content[0].cls += ' col-xs-' + this.labelxs;
32028                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32029                 }
32030                 
32031             }
32032         }
32033         
32034         var cfg = {
32035             tag : 'div',
32036             cls : 'row clearfix',
32037             cn : content
32038         };
32039         
32040         return cfg;
32041         
32042     },
32043     
32044     initEvents : function()
32045     {
32046         this.managerEl = this.el.select('.roo-document-manager', true).first();
32047         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32048         
32049         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32050         this.selectorEl.hide();
32051         
32052         if(this.multiple){
32053             this.selectorEl.attr('multiple', 'multiple');
32054         }
32055         
32056         this.selectorEl.on('change', this.onFileSelected, this);
32057         
32058         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32059         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32060         
32061         this.uploader.on('click', this.onUploaderClick, this);
32062         
32063         this.renderProgressDialog();
32064         
32065         var _this = this;
32066         
32067         window.addEventListener("resize", function() { _this.refresh(); } );
32068         
32069         this.fireEvent('initial', this);
32070     },
32071     
32072     renderProgressDialog : function()
32073     {
32074         var _this = this;
32075         
32076         this.progressDialog = new Roo.bootstrap.Modal({
32077             cls : 'roo-document-manager-progress-dialog',
32078             allow_close : false,
32079             animate : false,
32080             title : '',
32081             buttons : [
32082                 {
32083                     name  :'cancel',
32084                     weight : 'danger',
32085                     html : 'Cancel'
32086                 }
32087             ], 
32088             listeners : { 
32089                 btnclick : function() {
32090                     _this.uploadCancel();
32091                     this.hide();
32092                 }
32093             }
32094         });
32095          
32096         this.progressDialog.render(Roo.get(document.body));
32097          
32098         this.progress = new Roo.bootstrap.Progress({
32099             cls : 'roo-document-manager-progress',
32100             active : true,
32101             striped : true
32102         });
32103         
32104         this.progress.render(this.progressDialog.getChildContainer());
32105         
32106         this.progressBar = new Roo.bootstrap.ProgressBar({
32107             cls : 'roo-document-manager-progress-bar',
32108             aria_valuenow : 0,
32109             aria_valuemin : 0,
32110             aria_valuemax : 12,
32111             panel : 'success'
32112         });
32113         
32114         this.progressBar.render(this.progress.getChildContainer());
32115     },
32116     
32117     onUploaderClick : function(e)
32118     {
32119         e.preventDefault();
32120      
32121         if(this.fireEvent('beforeselectfile', this) != false){
32122             this.selectorEl.dom.click();
32123         }
32124         
32125     },
32126     
32127     onFileSelected : function(e)
32128     {
32129         e.preventDefault();
32130         
32131         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32132             return;
32133         }
32134         
32135         Roo.each(this.selectorEl.dom.files, function(file){
32136             if(this.fireEvent('inspect', this, file) != false){
32137                 this.files.push(file);
32138             }
32139         }, this);
32140         
32141         this.queue();
32142         
32143     },
32144     
32145     queue : function()
32146     {
32147         this.selectorEl.dom.value = '';
32148         
32149         if(!this.files || !this.files.length){
32150             return;
32151         }
32152         
32153         if(this.boxes > 0 && this.files.length > this.boxes){
32154             this.files = this.files.slice(0, this.boxes);
32155         }
32156         
32157         this.uploader.show();
32158         
32159         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32160             this.uploader.hide();
32161         }
32162         
32163         var _this = this;
32164         
32165         var files = [];
32166         
32167         var docs = [];
32168         
32169         Roo.each(this.files, function(file){
32170             
32171             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32172                 var f = this.renderPreview(file);
32173                 files.push(f);
32174                 return;
32175             }
32176             
32177             if(file.type.indexOf('image') != -1){
32178                 this.delegates.push(
32179                     (function(){
32180                         _this.process(file);
32181                     }).createDelegate(this)
32182                 );
32183         
32184                 return;
32185             }
32186             
32187             docs.push(
32188                 (function(){
32189                     _this.process(file);
32190                 }).createDelegate(this)
32191             );
32192             
32193         }, this);
32194         
32195         this.files = files;
32196         
32197         this.delegates = this.delegates.concat(docs);
32198         
32199         if(!this.delegates.length){
32200             this.refresh();
32201             return;
32202         }
32203         
32204         this.progressBar.aria_valuemax = this.delegates.length;
32205         
32206         this.arrange();
32207         
32208         return;
32209     },
32210     
32211     arrange : function()
32212     {
32213         if(!this.delegates.length){
32214             this.progressDialog.hide();
32215             this.refresh();
32216             return;
32217         }
32218         
32219         var delegate = this.delegates.shift();
32220         
32221         this.progressDialog.show();
32222         
32223         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32224         
32225         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32226         
32227         delegate();
32228     },
32229     
32230     refresh : function()
32231     {
32232         this.uploader.show();
32233         
32234         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32235             this.uploader.hide();
32236         }
32237         
32238         Roo.isTouch ? this.closable(false) : this.closable(true);
32239         
32240         this.fireEvent('refresh', this);
32241     },
32242     
32243     onRemove : function(e, el, o)
32244     {
32245         e.preventDefault();
32246         
32247         this.fireEvent('remove', this, o);
32248         
32249     },
32250     
32251     remove : function(o)
32252     {
32253         var files = [];
32254         
32255         Roo.each(this.files, function(file){
32256             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32257                 files.push(file);
32258                 return;
32259             }
32260
32261             o.target.remove();
32262
32263         }, this);
32264         
32265         this.files = files;
32266         
32267         this.refresh();
32268     },
32269     
32270     clear : function()
32271     {
32272         Roo.each(this.files, function(file){
32273             if(!file.target){
32274                 return;
32275             }
32276             
32277             file.target.remove();
32278
32279         }, this);
32280         
32281         this.files = [];
32282         
32283         this.refresh();
32284     },
32285     
32286     onClick : function(e, el, o)
32287     {
32288         e.preventDefault();
32289         
32290         this.fireEvent('click', this, o);
32291         
32292     },
32293     
32294     closable : function(closable)
32295     {
32296         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32297             
32298             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32299             
32300             if(closable){
32301                 el.show();
32302                 return;
32303             }
32304             
32305             el.hide();
32306             
32307         }, this);
32308     },
32309     
32310     xhrOnLoad : function(xhr)
32311     {
32312         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32313             el.remove();
32314         }, this);
32315         
32316         if (xhr.readyState !== 4) {
32317             this.arrange();
32318             this.fireEvent('exception', this, xhr);
32319             return;
32320         }
32321
32322         var response = Roo.decode(xhr.responseText);
32323         
32324         if(!response.success){
32325             this.arrange();
32326             this.fireEvent('exception', this, xhr);
32327             return;
32328         }
32329         
32330         var file = this.renderPreview(response.data);
32331         
32332         this.files.push(file);
32333         
32334         this.arrange();
32335         
32336         this.fireEvent('afterupload', this, xhr);
32337         
32338     },
32339     
32340     xhrOnError : function(xhr)
32341     {
32342         Roo.log('xhr on error');
32343         
32344         var response = Roo.decode(xhr.responseText);
32345           
32346         Roo.log(response);
32347         
32348         this.arrange();
32349     },
32350     
32351     process : function(file)
32352     {
32353         if(this.fireEvent('process', this, file) !== false){
32354             if(this.editable && file.type.indexOf('image') != -1){
32355                 this.fireEvent('edit', this, file);
32356                 return;
32357             }
32358
32359             this.uploadStart(file, false);
32360
32361             return;
32362         }
32363         
32364     },
32365     
32366     uploadStart : function(file, crop)
32367     {
32368         this.xhr = new XMLHttpRequest();
32369         
32370         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32371             this.arrange();
32372             return;
32373         }
32374         
32375         file.xhr = this.xhr;
32376             
32377         this.managerEl.createChild({
32378             tag : 'div',
32379             cls : 'roo-document-manager-loading',
32380             cn : [
32381                 {
32382                     tag : 'div',
32383                     tooltip : file.name,
32384                     cls : 'roo-document-manager-thumb',
32385                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32386                 }
32387             ]
32388
32389         });
32390
32391         this.xhr.open(this.method, this.url, true);
32392         
32393         var headers = {
32394             "Accept": "application/json",
32395             "Cache-Control": "no-cache",
32396             "X-Requested-With": "XMLHttpRequest"
32397         };
32398         
32399         for (var headerName in headers) {
32400             var headerValue = headers[headerName];
32401             if (headerValue) {
32402                 this.xhr.setRequestHeader(headerName, headerValue);
32403             }
32404         }
32405         
32406         var _this = this;
32407         
32408         this.xhr.onload = function()
32409         {
32410             _this.xhrOnLoad(_this.xhr);
32411         }
32412         
32413         this.xhr.onerror = function()
32414         {
32415             _this.xhrOnError(_this.xhr);
32416         }
32417         
32418         var formData = new FormData();
32419
32420         formData.append('returnHTML', 'NO');
32421         
32422         if(crop){
32423             formData.append('crop', crop);
32424         }
32425         
32426         formData.append(this.paramName, file, file.name);
32427         
32428         var options = {
32429             file : file, 
32430             manually : false
32431         };
32432         
32433         if(this.fireEvent('prepare', this, formData, options) != false){
32434             
32435             if(options.manually){
32436                 return;
32437             }
32438             
32439             this.xhr.send(formData);
32440             return;
32441         };
32442         
32443         this.uploadCancel();
32444     },
32445     
32446     uploadCancel : function()
32447     {
32448         if (this.xhr) {
32449             this.xhr.abort();
32450         }
32451         
32452         this.delegates = [];
32453         
32454         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32455             el.remove();
32456         }, this);
32457         
32458         this.arrange();
32459     },
32460     
32461     renderPreview : function(file)
32462     {
32463         if(typeof(file.target) != 'undefined' && file.target){
32464             return file;
32465         }
32466         
32467         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32468         
32469         var previewEl = this.managerEl.createChild({
32470             tag : 'div',
32471             cls : 'roo-document-manager-preview',
32472             cn : [
32473                 {
32474                     tag : 'div',
32475                     tooltip : file[this.toolTipName],
32476                     cls : 'roo-document-manager-thumb',
32477                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32478                 },
32479                 {
32480                     tag : 'button',
32481                     cls : 'close',
32482                     html : '<i class="fa fa-times-circle"></i>'
32483                 }
32484             ]
32485         });
32486
32487         var close = previewEl.select('button.close', true).first();
32488
32489         close.on('click', this.onRemove, this, file);
32490
32491         file.target = previewEl;
32492
32493         var image = previewEl.select('img', true).first();
32494         
32495         var _this = this;
32496         
32497         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32498         
32499         image.on('click', this.onClick, this, file);
32500         
32501         this.fireEvent('previewrendered', this, file);
32502         
32503         return file;
32504         
32505     },
32506     
32507     onPreviewLoad : function(file, image)
32508     {
32509         if(typeof(file.target) == 'undefined' || !file.target){
32510             return;
32511         }
32512         
32513         var width = image.dom.naturalWidth || image.dom.width;
32514         var height = image.dom.naturalHeight || image.dom.height;
32515         
32516         if(!this.previewResize) {
32517             return;
32518         }
32519         
32520         if(width > height){
32521             file.target.addClass('wide');
32522             return;
32523         }
32524         
32525         file.target.addClass('tall');
32526         return;
32527         
32528     },
32529     
32530     uploadFromSource : function(file, crop)
32531     {
32532         this.xhr = new XMLHttpRequest();
32533         
32534         this.managerEl.createChild({
32535             tag : 'div',
32536             cls : 'roo-document-manager-loading',
32537             cn : [
32538                 {
32539                     tag : 'div',
32540                     tooltip : file.name,
32541                     cls : 'roo-document-manager-thumb',
32542                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32543                 }
32544             ]
32545
32546         });
32547
32548         this.xhr.open(this.method, this.url, true);
32549         
32550         var headers = {
32551             "Accept": "application/json",
32552             "Cache-Control": "no-cache",
32553             "X-Requested-With": "XMLHttpRequest"
32554         };
32555         
32556         for (var headerName in headers) {
32557             var headerValue = headers[headerName];
32558             if (headerValue) {
32559                 this.xhr.setRequestHeader(headerName, headerValue);
32560             }
32561         }
32562         
32563         var _this = this;
32564         
32565         this.xhr.onload = function()
32566         {
32567             _this.xhrOnLoad(_this.xhr);
32568         }
32569         
32570         this.xhr.onerror = function()
32571         {
32572             _this.xhrOnError(_this.xhr);
32573         }
32574         
32575         var formData = new FormData();
32576
32577         formData.append('returnHTML', 'NO');
32578         
32579         formData.append('crop', crop);
32580         
32581         if(typeof(file.filename) != 'undefined'){
32582             formData.append('filename', file.filename);
32583         }
32584         
32585         if(typeof(file.mimetype) != 'undefined'){
32586             formData.append('mimetype', file.mimetype);
32587         }
32588         
32589         Roo.log(formData);
32590         
32591         if(this.fireEvent('prepare', this, formData) != false){
32592             this.xhr.send(formData);
32593         };
32594     }
32595 });
32596
32597 /*
32598 * Licence: LGPL
32599 */
32600
32601 /**
32602  * @class Roo.bootstrap.DocumentViewer
32603  * @extends Roo.bootstrap.Component
32604  * Bootstrap DocumentViewer class
32605  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32606  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32607  * 
32608  * @constructor
32609  * Create a new DocumentViewer
32610  * @param {Object} config The config object
32611  */
32612
32613 Roo.bootstrap.DocumentViewer = function(config){
32614     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32615     
32616     this.addEvents({
32617         /**
32618          * @event initial
32619          * Fire after initEvent
32620          * @param {Roo.bootstrap.DocumentViewer} this
32621          */
32622         "initial" : true,
32623         /**
32624          * @event click
32625          * Fire after click
32626          * @param {Roo.bootstrap.DocumentViewer} this
32627          */
32628         "click" : true,
32629         /**
32630          * @event download
32631          * Fire after download button
32632          * @param {Roo.bootstrap.DocumentViewer} this
32633          */
32634         "download" : true,
32635         /**
32636          * @event trash
32637          * Fire after trash button
32638          * @param {Roo.bootstrap.DocumentViewer} this
32639          */
32640         "trash" : true
32641         
32642     });
32643 };
32644
32645 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32646     
32647     showDownload : true,
32648     
32649     showTrash : true,
32650     
32651     getAutoCreate : function()
32652     {
32653         var cfg = {
32654             tag : 'div',
32655             cls : 'roo-document-viewer',
32656             cn : [
32657                 {
32658                     tag : 'div',
32659                     cls : 'roo-document-viewer-body',
32660                     cn : [
32661                         {
32662                             tag : 'div',
32663                             cls : 'roo-document-viewer-thumb',
32664                             cn : [
32665                                 {
32666                                     tag : 'img',
32667                                     cls : 'roo-document-viewer-image'
32668                                 }
32669                             ]
32670                         }
32671                     ]
32672                 },
32673                 {
32674                     tag : 'div',
32675                     cls : 'roo-document-viewer-footer',
32676                     cn : {
32677                         tag : 'div',
32678                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32679                         cn : [
32680                             {
32681                                 tag : 'div',
32682                                 cls : 'btn-group roo-document-viewer-download',
32683                                 cn : [
32684                                     {
32685                                         tag : 'button',
32686                                         cls : 'btn btn-default',
32687                                         html : '<i class="fa fa-download"></i>'
32688                                     }
32689                                 ]
32690                             },
32691                             {
32692                                 tag : 'div',
32693                                 cls : 'btn-group roo-document-viewer-trash',
32694                                 cn : [
32695                                     {
32696                                         tag : 'button',
32697                                         cls : 'btn btn-default',
32698                                         html : '<i class="fa fa-trash"></i>'
32699                                     }
32700                                 ]
32701                             }
32702                         ]
32703                     }
32704                 }
32705             ]
32706         };
32707         
32708         return cfg;
32709     },
32710     
32711     initEvents : function()
32712     {
32713         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32714         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32715         
32716         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32717         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32718         
32719         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32720         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32721         
32722         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32723         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32724         
32725         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32726         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32727         
32728         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32729         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32730         
32731         this.bodyEl.on('click', this.onClick, this);
32732         this.downloadBtn.on('click', this.onDownload, this);
32733         this.trashBtn.on('click', this.onTrash, this);
32734         
32735         this.downloadBtn.hide();
32736         this.trashBtn.hide();
32737         
32738         if(this.showDownload){
32739             this.downloadBtn.show();
32740         }
32741         
32742         if(this.showTrash){
32743             this.trashBtn.show();
32744         }
32745         
32746         if(!this.showDownload && !this.showTrash) {
32747             this.footerEl.hide();
32748         }
32749         
32750     },
32751     
32752     initial : function()
32753     {
32754         this.fireEvent('initial', this);
32755         
32756     },
32757     
32758     onClick : function(e)
32759     {
32760         e.preventDefault();
32761         
32762         this.fireEvent('click', this);
32763     },
32764     
32765     onDownload : function(e)
32766     {
32767         e.preventDefault();
32768         
32769         this.fireEvent('download', this);
32770     },
32771     
32772     onTrash : function(e)
32773     {
32774         e.preventDefault();
32775         
32776         this.fireEvent('trash', this);
32777     }
32778     
32779 });
32780 /*
32781  * - LGPL
32782  *
32783  * nav progress bar
32784  * 
32785  */
32786
32787 /**
32788  * @class Roo.bootstrap.NavProgressBar
32789  * @extends Roo.bootstrap.Component
32790  * Bootstrap NavProgressBar class
32791  * 
32792  * @constructor
32793  * Create a new nav progress bar
32794  * @param {Object} config The config object
32795  */
32796
32797 Roo.bootstrap.NavProgressBar = function(config){
32798     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32799
32800     this.bullets = this.bullets || [];
32801    
32802 //    Roo.bootstrap.NavProgressBar.register(this);
32803      this.addEvents({
32804         /**
32805              * @event changed
32806              * Fires when the active item changes
32807              * @param {Roo.bootstrap.NavProgressBar} this
32808              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32809              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32810          */
32811         'changed': true
32812      });
32813     
32814 };
32815
32816 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32817     
32818     bullets : [],
32819     barItems : [],
32820     
32821     getAutoCreate : function()
32822     {
32823         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32824         
32825         cfg = {
32826             tag : 'div',
32827             cls : 'roo-navigation-bar-group',
32828             cn : [
32829                 {
32830                     tag : 'div',
32831                     cls : 'roo-navigation-top-bar'
32832                 },
32833                 {
32834                     tag : 'div',
32835                     cls : 'roo-navigation-bullets-bar',
32836                     cn : [
32837                         {
32838                             tag : 'ul',
32839                             cls : 'roo-navigation-bar'
32840                         }
32841                     ]
32842                 },
32843                 
32844                 {
32845                     tag : 'div',
32846                     cls : 'roo-navigation-bottom-bar'
32847                 }
32848             ]
32849             
32850         };
32851         
32852         return cfg;
32853         
32854     },
32855     
32856     initEvents: function() 
32857     {
32858         
32859     },
32860     
32861     onRender : function(ct, position) 
32862     {
32863         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32864         
32865         if(this.bullets.length){
32866             Roo.each(this.bullets, function(b){
32867                this.addItem(b);
32868             }, this);
32869         }
32870         
32871         this.format();
32872         
32873     },
32874     
32875     addItem : function(cfg)
32876     {
32877         var item = new Roo.bootstrap.NavProgressItem(cfg);
32878         
32879         item.parentId = this.id;
32880         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32881         
32882         if(cfg.html){
32883             var top = new Roo.bootstrap.Element({
32884                 tag : 'div',
32885                 cls : 'roo-navigation-bar-text'
32886             });
32887             
32888             var bottom = new Roo.bootstrap.Element({
32889                 tag : 'div',
32890                 cls : 'roo-navigation-bar-text'
32891             });
32892             
32893             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32894             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32895             
32896             var topText = new Roo.bootstrap.Element({
32897                 tag : 'span',
32898                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32899             });
32900             
32901             var bottomText = new Roo.bootstrap.Element({
32902                 tag : 'span',
32903                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32904             });
32905             
32906             topText.onRender(top.el, null);
32907             bottomText.onRender(bottom.el, null);
32908             
32909             item.topEl = top;
32910             item.bottomEl = bottom;
32911         }
32912         
32913         this.barItems.push(item);
32914         
32915         return item;
32916     },
32917     
32918     getActive : function()
32919     {
32920         var active = false;
32921         
32922         Roo.each(this.barItems, function(v){
32923             
32924             if (!v.isActive()) {
32925                 return;
32926             }
32927             
32928             active = v;
32929             return false;
32930             
32931         });
32932         
32933         return active;
32934     },
32935     
32936     setActiveItem : function(item)
32937     {
32938         var prev = false;
32939         
32940         Roo.each(this.barItems, function(v){
32941             if (v.rid == item.rid) {
32942                 return ;
32943             }
32944             
32945             if (v.isActive()) {
32946                 v.setActive(false);
32947                 prev = v;
32948             }
32949         });
32950
32951         item.setActive(true);
32952         
32953         this.fireEvent('changed', this, item, prev);
32954     },
32955     
32956     getBarItem: function(rid)
32957     {
32958         var ret = false;
32959         
32960         Roo.each(this.barItems, function(e) {
32961             if (e.rid != rid) {
32962                 return;
32963             }
32964             
32965             ret =  e;
32966             return false;
32967         });
32968         
32969         return ret;
32970     },
32971     
32972     indexOfItem : function(item)
32973     {
32974         var index = false;
32975         
32976         Roo.each(this.barItems, function(v, i){
32977             
32978             if (v.rid != item.rid) {
32979                 return;
32980             }
32981             
32982             index = i;
32983             return false
32984         });
32985         
32986         return index;
32987     },
32988     
32989     setActiveNext : function()
32990     {
32991         var i = this.indexOfItem(this.getActive());
32992         
32993         if (i > this.barItems.length) {
32994             return;
32995         }
32996         
32997         this.setActiveItem(this.barItems[i+1]);
32998     },
32999     
33000     setActivePrev : function()
33001     {
33002         var i = this.indexOfItem(this.getActive());
33003         
33004         if (i  < 1) {
33005             return;
33006         }
33007         
33008         this.setActiveItem(this.barItems[i-1]);
33009     },
33010     
33011     format : function()
33012     {
33013         if(!this.barItems.length){
33014             return;
33015         }
33016      
33017         var width = 100 / this.barItems.length;
33018         
33019         Roo.each(this.barItems, function(i){
33020             i.el.setStyle('width', width + '%');
33021             i.topEl.el.setStyle('width', width + '%');
33022             i.bottomEl.el.setStyle('width', width + '%');
33023         }, this);
33024         
33025     }
33026     
33027 });
33028 /*
33029  * - LGPL
33030  *
33031  * Nav Progress Item
33032  * 
33033  */
33034
33035 /**
33036  * @class Roo.bootstrap.NavProgressItem
33037  * @extends Roo.bootstrap.Component
33038  * Bootstrap NavProgressItem class
33039  * @cfg {String} rid the reference id
33040  * @cfg {Boolean} active (true|false) Is item active default false
33041  * @cfg {Boolean} disabled (true|false) Is item active default false
33042  * @cfg {String} html
33043  * @cfg {String} position (top|bottom) text position default bottom
33044  * @cfg {String} icon show icon instead of number
33045  * 
33046  * @constructor
33047  * Create a new NavProgressItem
33048  * @param {Object} config The config object
33049  */
33050 Roo.bootstrap.NavProgressItem = function(config){
33051     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33052     this.addEvents({
33053         // raw events
33054         /**
33055          * @event click
33056          * The raw click event for the entire grid.
33057          * @param {Roo.bootstrap.NavProgressItem} this
33058          * @param {Roo.EventObject} e
33059          */
33060         "click" : true
33061     });
33062    
33063 };
33064
33065 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33066     
33067     rid : '',
33068     active : false,
33069     disabled : false,
33070     html : '',
33071     position : 'bottom',
33072     icon : false,
33073     
33074     getAutoCreate : function()
33075     {
33076         var iconCls = 'roo-navigation-bar-item-icon';
33077         
33078         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33079         
33080         var cfg = {
33081             tag: 'li',
33082             cls: 'roo-navigation-bar-item',
33083             cn : [
33084                 {
33085                     tag : 'i',
33086                     cls : iconCls
33087                 }
33088             ]
33089         };
33090         
33091         if(this.active){
33092             cfg.cls += ' active';
33093         }
33094         if(this.disabled){
33095             cfg.cls += ' disabled';
33096         }
33097         
33098         return cfg;
33099     },
33100     
33101     disable : function()
33102     {
33103         this.setDisabled(true);
33104     },
33105     
33106     enable : function()
33107     {
33108         this.setDisabled(false);
33109     },
33110     
33111     initEvents: function() 
33112     {
33113         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33114         
33115         this.iconEl.on('click', this.onClick, this);
33116     },
33117     
33118     onClick : function(e)
33119     {
33120         e.preventDefault();
33121         
33122         if(this.disabled){
33123             return;
33124         }
33125         
33126         if(this.fireEvent('click', this, e) === false){
33127             return;
33128         };
33129         
33130         this.parent().setActiveItem(this);
33131     },
33132     
33133     isActive: function () 
33134     {
33135         return this.active;
33136     },
33137     
33138     setActive : function(state)
33139     {
33140         if(this.active == state){
33141             return;
33142         }
33143         
33144         this.active = state;
33145         
33146         if (state) {
33147             this.el.addClass('active');
33148             return;
33149         }
33150         
33151         this.el.removeClass('active');
33152         
33153         return;
33154     },
33155     
33156     setDisabled : function(state)
33157     {
33158         if(this.disabled == state){
33159             return;
33160         }
33161         
33162         this.disabled = state;
33163         
33164         if (state) {
33165             this.el.addClass('disabled');
33166             return;
33167         }
33168         
33169         this.el.removeClass('disabled');
33170     },
33171     
33172     tooltipEl : function()
33173     {
33174         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33175     }
33176 });
33177  
33178
33179  /*
33180  * - LGPL
33181  *
33182  * FieldLabel
33183  * 
33184  */
33185
33186 /**
33187  * @class Roo.bootstrap.FieldLabel
33188  * @extends Roo.bootstrap.Component
33189  * Bootstrap FieldLabel class
33190  * @cfg {String} html contents of the element
33191  * @cfg {String} tag tag of the element default label
33192  * @cfg {String} cls class of the element
33193  * @cfg {String} target label target 
33194  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33195  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33196  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33197  * @cfg {String} iconTooltip default "This field is required"
33198  * @cfg {String} indicatorpos (left|right) default left
33199  * 
33200  * @constructor
33201  * Create a new FieldLabel
33202  * @param {Object} config The config object
33203  */
33204
33205 Roo.bootstrap.FieldLabel = function(config){
33206     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33207     
33208     this.addEvents({
33209             /**
33210              * @event invalid
33211              * Fires after the field has been marked as invalid.
33212              * @param {Roo.form.FieldLabel} this
33213              * @param {String} msg The validation message
33214              */
33215             invalid : true,
33216             /**
33217              * @event valid
33218              * Fires after the field has been validated with no errors.
33219              * @param {Roo.form.FieldLabel} this
33220              */
33221             valid : true
33222         });
33223 };
33224
33225 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33226     
33227     tag: 'label',
33228     cls: '',
33229     html: '',
33230     target: '',
33231     allowBlank : true,
33232     invalidClass : 'has-warning',
33233     validClass : 'has-success',
33234     iconTooltip : 'This field is required',
33235     indicatorpos : 'left',
33236     
33237     getAutoCreate : function(){
33238         
33239         var cls = "";
33240         if (!this.allowBlank) {
33241             cls  = "visible";
33242         }
33243         
33244         var cfg = {
33245             tag : this.tag,
33246             cls : 'roo-bootstrap-field-label ' + this.cls,
33247             for : this.target,
33248             cn : [
33249                 {
33250                     tag : 'i',
33251                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33252                     tooltip : this.iconTooltip
33253                 },
33254                 {
33255                     tag : 'span',
33256                     html : this.html
33257                 }
33258             ] 
33259         };
33260         
33261         if(this.indicatorpos == 'right'){
33262             var cfg = {
33263                 tag : this.tag,
33264                 cls : 'roo-bootstrap-field-label ' + this.cls,
33265                 for : this.target,
33266                 cn : [
33267                     {
33268                         tag : 'span',
33269                         html : this.html
33270                     },
33271                     {
33272                         tag : 'i',
33273                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33274                         tooltip : this.iconTooltip
33275                     }
33276                 ] 
33277             };
33278         }
33279         
33280         return cfg;
33281     },
33282     
33283     initEvents: function() 
33284     {
33285         Roo.bootstrap.Element.superclass.initEvents.call(this);
33286         
33287         this.indicator = this.indicatorEl();
33288         
33289         if(this.indicator){
33290             this.indicator.removeClass('visible');
33291             this.indicator.addClass('invisible');
33292         }
33293         
33294         Roo.bootstrap.FieldLabel.register(this);
33295     },
33296     
33297     indicatorEl : function()
33298     {
33299         var indicator = this.el.select('i.roo-required-indicator',true).first();
33300         
33301         if(!indicator){
33302             return false;
33303         }
33304         
33305         return indicator;
33306         
33307     },
33308     
33309     /**
33310      * Mark this field as valid
33311      */
33312     markValid : function()
33313     {
33314         if(this.indicator){
33315             this.indicator.removeClass('visible');
33316             this.indicator.addClass('invisible');
33317         }
33318         if (Roo.bootstrap.version == 3) {
33319             this.el.removeClass(this.invalidClass);
33320             this.el.addClass(this.validClass);
33321         } else {
33322             this.el.removeClass('is-invalid');
33323             this.el.addClass('is-valid');
33324         }
33325         
33326         
33327         this.fireEvent('valid', this);
33328     },
33329     
33330     /**
33331      * Mark this field as invalid
33332      * @param {String} msg The validation message
33333      */
33334     markInvalid : function(msg)
33335     {
33336         if(this.indicator){
33337             this.indicator.removeClass('invisible');
33338             this.indicator.addClass('visible');
33339         }
33340           if (Roo.bootstrap.version == 3) {
33341             this.el.removeClass(this.validClass);
33342             this.el.addClass(this.invalidClass);
33343         } else {
33344             this.el.removeClass('is-valid');
33345             this.el.addClass('is-invalid');
33346         }
33347         
33348         
33349         this.fireEvent('invalid', this, msg);
33350     }
33351     
33352    
33353 });
33354
33355 Roo.apply(Roo.bootstrap.FieldLabel, {
33356     
33357     groups: {},
33358     
33359      /**
33360     * register a FieldLabel Group
33361     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33362     */
33363     register : function(label)
33364     {
33365         if(this.groups.hasOwnProperty(label.target)){
33366             return;
33367         }
33368      
33369         this.groups[label.target] = label;
33370         
33371     },
33372     /**
33373     * fetch a FieldLabel Group based on the target
33374     * @param {string} target
33375     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33376     */
33377     get: function(target) {
33378         if (typeof(this.groups[target]) == 'undefined') {
33379             return false;
33380         }
33381         
33382         return this.groups[target] ;
33383     }
33384 });
33385
33386  
33387
33388  /*
33389  * - LGPL
33390  *
33391  * page DateSplitField.
33392  * 
33393  */
33394
33395
33396 /**
33397  * @class Roo.bootstrap.DateSplitField
33398  * @extends Roo.bootstrap.Component
33399  * Bootstrap DateSplitField class
33400  * @cfg {string} fieldLabel - the label associated
33401  * @cfg {Number} labelWidth set the width of label (0-12)
33402  * @cfg {String} labelAlign (top|left)
33403  * @cfg {Boolean} dayAllowBlank (true|false) default false
33404  * @cfg {Boolean} monthAllowBlank (true|false) default false
33405  * @cfg {Boolean} yearAllowBlank (true|false) default false
33406  * @cfg {string} dayPlaceholder 
33407  * @cfg {string} monthPlaceholder
33408  * @cfg {string} yearPlaceholder
33409  * @cfg {string} dayFormat default 'd'
33410  * @cfg {string} monthFormat default 'm'
33411  * @cfg {string} yearFormat default 'Y'
33412  * @cfg {Number} labellg set the width of label (1-12)
33413  * @cfg {Number} labelmd set the width of label (1-12)
33414  * @cfg {Number} labelsm set the width of label (1-12)
33415  * @cfg {Number} labelxs set the width of label (1-12)
33416
33417  *     
33418  * @constructor
33419  * Create a new DateSplitField
33420  * @param {Object} config The config object
33421  */
33422
33423 Roo.bootstrap.DateSplitField = function(config){
33424     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33425     
33426     this.addEvents({
33427         // raw events
33428          /**
33429          * @event years
33430          * getting the data of years
33431          * @param {Roo.bootstrap.DateSplitField} this
33432          * @param {Object} years
33433          */
33434         "years" : true,
33435         /**
33436          * @event days
33437          * getting the data of days
33438          * @param {Roo.bootstrap.DateSplitField} this
33439          * @param {Object} days
33440          */
33441         "days" : true,
33442         /**
33443          * @event invalid
33444          * Fires after the field has been marked as invalid.
33445          * @param {Roo.form.Field} this
33446          * @param {String} msg The validation message
33447          */
33448         invalid : true,
33449        /**
33450          * @event valid
33451          * Fires after the field has been validated with no errors.
33452          * @param {Roo.form.Field} this
33453          */
33454         valid : true
33455     });
33456 };
33457
33458 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33459     
33460     fieldLabel : '',
33461     labelAlign : 'top',
33462     labelWidth : 3,
33463     dayAllowBlank : false,
33464     monthAllowBlank : false,
33465     yearAllowBlank : false,
33466     dayPlaceholder : '',
33467     monthPlaceholder : '',
33468     yearPlaceholder : '',
33469     dayFormat : 'd',
33470     monthFormat : 'm',
33471     yearFormat : 'Y',
33472     isFormField : true,
33473     labellg : 0,
33474     labelmd : 0,
33475     labelsm : 0,
33476     labelxs : 0,
33477     
33478     getAutoCreate : function()
33479     {
33480         var cfg = {
33481             tag : 'div',
33482             cls : 'row roo-date-split-field-group',
33483             cn : [
33484                 {
33485                     tag : 'input',
33486                     type : 'hidden',
33487                     cls : 'form-hidden-field roo-date-split-field-group-value',
33488                     name : this.name
33489                 }
33490             ]
33491         };
33492         
33493         var labelCls = 'col-md-12';
33494         var contentCls = 'col-md-4';
33495         
33496         if(this.fieldLabel){
33497             
33498             var label = {
33499                 tag : 'div',
33500                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33501                 cn : [
33502                     {
33503                         tag : 'label',
33504                         html : this.fieldLabel
33505                     }
33506                 ]
33507             };
33508             
33509             if(this.labelAlign == 'left'){
33510             
33511                 if(this.labelWidth > 12){
33512                     label.style = "width: " + this.labelWidth + 'px';
33513                 }
33514
33515                 if(this.labelWidth < 13 && this.labelmd == 0){
33516                     this.labelmd = this.labelWidth;
33517                 }
33518
33519                 if(this.labellg > 0){
33520                     labelCls = ' col-lg-' + this.labellg;
33521                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33522                 }
33523
33524                 if(this.labelmd > 0){
33525                     labelCls = ' col-md-' + this.labelmd;
33526                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33527                 }
33528
33529                 if(this.labelsm > 0){
33530                     labelCls = ' col-sm-' + this.labelsm;
33531                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33532                 }
33533
33534                 if(this.labelxs > 0){
33535                     labelCls = ' col-xs-' + this.labelxs;
33536                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33537                 }
33538             }
33539             
33540             label.cls += ' ' + labelCls;
33541             
33542             cfg.cn.push(label);
33543         }
33544         
33545         Roo.each(['day', 'month', 'year'], function(t){
33546             cfg.cn.push({
33547                 tag : 'div',
33548                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33549             });
33550         }, this);
33551         
33552         return cfg;
33553     },
33554     
33555     inputEl: function ()
33556     {
33557         return this.el.select('.roo-date-split-field-group-value', true).first();
33558     },
33559     
33560     onRender : function(ct, position) 
33561     {
33562         var _this = this;
33563         
33564         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33565         
33566         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33567         
33568         this.dayField = new Roo.bootstrap.ComboBox({
33569             allowBlank : this.dayAllowBlank,
33570             alwaysQuery : true,
33571             displayField : 'value',
33572             editable : false,
33573             fieldLabel : '',
33574             forceSelection : true,
33575             mode : 'local',
33576             placeholder : this.dayPlaceholder,
33577             selectOnFocus : true,
33578             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33579             triggerAction : 'all',
33580             typeAhead : true,
33581             valueField : 'value',
33582             store : new Roo.data.SimpleStore({
33583                 data : (function() {    
33584                     var days = [];
33585                     _this.fireEvent('days', _this, days);
33586                     return days;
33587                 })(),
33588                 fields : [ 'value' ]
33589             }),
33590             listeners : {
33591                 select : function (_self, record, index)
33592                 {
33593                     _this.setValue(_this.getValue());
33594                 }
33595             }
33596         });
33597
33598         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33599         
33600         this.monthField = new Roo.bootstrap.MonthField({
33601             after : '<i class=\"fa fa-calendar\"></i>',
33602             allowBlank : this.monthAllowBlank,
33603             placeholder : this.monthPlaceholder,
33604             readOnly : true,
33605             listeners : {
33606                 render : function (_self)
33607                 {
33608                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33609                         e.preventDefault();
33610                         _self.focus();
33611                     });
33612                 },
33613                 select : function (_self, oldvalue, newvalue)
33614                 {
33615                     _this.setValue(_this.getValue());
33616                 }
33617             }
33618         });
33619         
33620         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33621         
33622         this.yearField = new Roo.bootstrap.ComboBox({
33623             allowBlank : this.yearAllowBlank,
33624             alwaysQuery : true,
33625             displayField : 'value',
33626             editable : false,
33627             fieldLabel : '',
33628             forceSelection : true,
33629             mode : 'local',
33630             placeholder : this.yearPlaceholder,
33631             selectOnFocus : true,
33632             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33633             triggerAction : 'all',
33634             typeAhead : true,
33635             valueField : 'value',
33636             store : new Roo.data.SimpleStore({
33637                 data : (function() {
33638                     var years = [];
33639                     _this.fireEvent('years', _this, years);
33640                     return years;
33641                 })(),
33642                 fields : [ 'value' ]
33643             }),
33644             listeners : {
33645                 select : function (_self, record, index)
33646                 {
33647                     _this.setValue(_this.getValue());
33648                 }
33649             }
33650         });
33651
33652         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33653     },
33654     
33655     setValue : function(v, format)
33656     {
33657         this.inputEl.dom.value = v;
33658         
33659         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33660         
33661         var d = Date.parseDate(v, f);
33662         
33663         if(!d){
33664             this.validate();
33665             return;
33666         }
33667         
33668         this.setDay(d.format(this.dayFormat));
33669         this.setMonth(d.format(this.monthFormat));
33670         this.setYear(d.format(this.yearFormat));
33671         
33672         this.validate();
33673         
33674         return;
33675     },
33676     
33677     setDay : function(v)
33678     {
33679         this.dayField.setValue(v);
33680         this.inputEl.dom.value = this.getValue();
33681         this.validate();
33682         return;
33683     },
33684     
33685     setMonth : function(v)
33686     {
33687         this.monthField.setValue(v, true);
33688         this.inputEl.dom.value = this.getValue();
33689         this.validate();
33690         return;
33691     },
33692     
33693     setYear : function(v)
33694     {
33695         this.yearField.setValue(v);
33696         this.inputEl.dom.value = this.getValue();
33697         this.validate();
33698         return;
33699     },
33700     
33701     getDay : function()
33702     {
33703         return this.dayField.getValue();
33704     },
33705     
33706     getMonth : function()
33707     {
33708         return this.monthField.getValue();
33709     },
33710     
33711     getYear : function()
33712     {
33713         return this.yearField.getValue();
33714     },
33715     
33716     getValue : function()
33717     {
33718         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33719         
33720         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33721         
33722         return date;
33723     },
33724     
33725     reset : function()
33726     {
33727         this.setDay('');
33728         this.setMonth('');
33729         this.setYear('');
33730         this.inputEl.dom.value = '';
33731         this.validate();
33732         return;
33733     },
33734     
33735     validate : function()
33736     {
33737         var d = this.dayField.validate();
33738         var m = this.monthField.validate();
33739         var y = this.yearField.validate();
33740         
33741         var valid = true;
33742         
33743         if(
33744                 (!this.dayAllowBlank && !d) ||
33745                 (!this.monthAllowBlank && !m) ||
33746                 (!this.yearAllowBlank && !y)
33747         ){
33748             valid = false;
33749         }
33750         
33751         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33752             return valid;
33753         }
33754         
33755         if(valid){
33756             this.markValid();
33757             return valid;
33758         }
33759         
33760         this.markInvalid();
33761         
33762         return valid;
33763     },
33764     
33765     markValid : function()
33766     {
33767         
33768         var label = this.el.select('label', true).first();
33769         var icon = this.el.select('i.fa-star', true).first();
33770
33771         if(label && icon){
33772             icon.remove();
33773         }
33774         
33775         this.fireEvent('valid', this);
33776     },
33777     
33778      /**
33779      * Mark this field as invalid
33780      * @param {String} msg The validation message
33781      */
33782     markInvalid : function(msg)
33783     {
33784         
33785         var label = this.el.select('label', true).first();
33786         var icon = this.el.select('i.fa-star', true).first();
33787
33788         if(label && !icon){
33789             this.el.select('.roo-date-split-field-label', true).createChild({
33790                 tag : 'i',
33791                 cls : 'text-danger fa fa-lg fa-star',
33792                 tooltip : 'This field is required',
33793                 style : 'margin-right:5px;'
33794             }, label, true);
33795         }
33796         
33797         this.fireEvent('invalid', this, msg);
33798     },
33799     
33800     clearInvalid : function()
33801     {
33802         var label = this.el.select('label', true).first();
33803         var icon = this.el.select('i.fa-star', true).first();
33804
33805         if(label && icon){
33806             icon.remove();
33807         }
33808         
33809         this.fireEvent('valid', this);
33810     },
33811     
33812     getName: function()
33813     {
33814         return this.name;
33815     }
33816     
33817 });
33818
33819  /**
33820  *
33821  * This is based on 
33822  * http://masonry.desandro.com
33823  *
33824  * The idea is to render all the bricks based on vertical width...
33825  *
33826  * The original code extends 'outlayer' - we might need to use that....
33827  * 
33828  */
33829
33830
33831 /**
33832  * @class Roo.bootstrap.LayoutMasonry
33833  * @extends Roo.bootstrap.Component
33834  * Bootstrap Layout Masonry class
33835  * 
33836  * @constructor
33837  * Create a new Element
33838  * @param {Object} config The config object
33839  */
33840
33841 Roo.bootstrap.LayoutMasonry = function(config){
33842     
33843     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33844     
33845     this.bricks = [];
33846     
33847     Roo.bootstrap.LayoutMasonry.register(this);
33848     
33849     this.addEvents({
33850         // raw events
33851         /**
33852          * @event layout
33853          * Fire after layout the items
33854          * @param {Roo.bootstrap.LayoutMasonry} this
33855          * @param {Roo.EventObject} e
33856          */
33857         "layout" : true
33858     });
33859     
33860 };
33861
33862 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33863     
33864     /**
33865      * @cfg {Boolean} isLayoutInstant = no animation?
33866      */   
33867     isLayoutInstant : false, // needed?
33868    
33869     /**
33870      * @cfg {Number} boxWidth  width of the columns
33871      */   
33872     boxWidth : 450,
33873     
33874       /**
33875      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33876      */   
33877     boxHeight : 0,
33878     
33879     /**
33880      * @cfg {Number} padWidth padding below box..
33881      */   
33882     padWidth : 10, 
33883     
33884     /**
33885      * @cfg {Number} gutter gutter width..
33886      */   
33887     gutter : 10,
33888     
33889      /**
33890      * @cfg {Number} maxCols maximum number of columns
33891      */   
33892     
33893     maxCols: 0,
33894     
33895     /**
33896      * @cfg {Boolean} isAutoInitial defalut true
33897      */   
33898     isAutoInitial : true, 
33899     
33900     containerWidth: 0,
33901     
33902     /**
33903      * @cfg {Boolean} isHorizontal defalut false
33904      */   
33905     isHorizontal : false, 
33906
33907     currentSize : null,
33908     
33909     tag: 'div',
33910     
33911     cls: '',
33912     
33913     bricks: null, //CompositeElement
33914     
33915     cols : 1,
33916     
33917     _isLayoutInited : false,
33918     
33919 //    isAlternative : false, // only use for vertical layout...
33920     
33921     /**
33922      * @cfg {Number} alternativePadWidth padding below box..
33923      */   
33924     alternativePadWidth : 50,
33925     
33926     selectedBrick : [],
33927     
33928     getAutoCreate : function(){
33929         
33930         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33931         
33932         var cfg = {
33933             tag: this.tag,
33934             cls: 'blog-masonary-wrapper ' + this.cls,
33935             cn : {
33936                 cls : 'mas-boxes masonary'
33937             }
33938         };
33939         
33940         return cfg;
33941     },
33942     
33943     getChildContainer: function( )
33944     {
33945         if (this.boxesEl) {
33946             return this.boxesEl;
33947         }
33948         
33949         this.boxesEl = this.el.select('.mas-boxes').first();
33950         
33951         return this.boxesEl;
33952     },
33953     
33954     
33955     initEvents : function()
33956     {
33957         var _this = this;
33958         
33959         if(this.isAutoInitial){
33960             Roo.log('hook children rendered');
33961             this.on('childrenrendered', function() {
33962                 Roo.log('children rendered');
33963                 _this.initial();
33964             } ,this);
33965         }
33966     },
33967     
33968     initial : function()
33969     {
33970         this.selectedBrick = [];
33971         
33972         this.currentSize = this.el.getBox(true);
33973         
33974         Roo.EventManager.onWindowResize(this.resize, this); 
33975
33976         if(!this.isAutoInitial){
33977             this.layout();
33978             return;
33979         }
33980         
33981         this.layout();
33982         
33983         return;
33984         //this.layout.defer(500,this);
33985         
33986     },
33987     
33988     resize : function()
33989     {
33990         var cs = this.el.getBox(true);
33991         
33992         if (
33993                 this.currentSize.width == cs.width && 
33994                 this.currentSize.x == cs.x && 
33995                 this.currentSize.height == cs.height && 
33996                 this.currentSize.y == cs.y 
33997         ) {
33998             Roo.log("no change in with or X or Y");
33999             return;
34000         }
34001         
34002         this.currentSize = cs;
34003         
34004         this.layout();
34005         
34006     },
34007     
34008     layout : function()
34009     {   
34010         this._resetLayout();
34011         
34012         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34013         
34014         this.layoutItems( isInstant );
34015       
34016         this._isLayoutInited = true;
34017         
34018         this.fireEvent('layout', this);
34019         
34020     },
34021     
34022     _resetLayout : function()
34023     {
34024         if(this.isHorizontal){
34025             this.horizontalMeasureColumns();
34026             return;
34027         }
34028         
34029         this.verticalMeasureColumns();
34030         
34031     },
34032     
34033     verticalMeasureColumns : function()
34034     {
34035         this.getContainerWidth();
34036         
34037 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34038 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34039 //            return;
34040 //        }
34041         
34042         var boxWidth = this.boxWidth + this.padWidth;
34043         
34044         if(this.containerWidth < this.boxWidth){
34045             boxWidth = this.containerWidth
34046         }
34047         
34048         var containerWidth = this.containerWidth;
34049         
34050         var cols = Math.floor(containerWidth / boxWidth);
34051         
34052         this.cols = Math.max( cols, 1 );
34053         
34054         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34055         
34056         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34057         
34058         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34059         
34060         this.colWidth = boxWidth + avail - this.padWidth;
34061         
34062         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34063         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34064     },
34065     
34066     horizontalMeasureColumns : function()
34067     {
34068         this.getContainerWidth();
34069         
34070         var boxWidth = this.boxWidth;
34071         
34072         if(this.containerWidth < boxWidth){
34073             boxWidth = this.containerWidth;
34074         }
34075         
34076         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34077         
34078         this.el.setHeight(boxWidth);
34079         
34080     },
34081     
34082     getContainerWidth : function()
34083     {
34084         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34085     },
34086     
34087     layoutItems : function( isInstant )
34088     {
34089         Roo.log(this.bricks);
34090         
34091         var items = Roo.apply([], this.bricks);
34092         
34093         if(this.isHorizontal){
34094             this._horizontalLayoutItems( items , isInstant );
34095             return;
34096         }
34097         
34098 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34099 //            this._verticalAlternativeLayoutItems( items , isInstant );
34100 //            return;
34101 //        }
34102         
34103         this._verticalLayoutItems( items , isInstant );
34104         
34105     },
34106     
34107     _verticalLayoutItems : function ( items , isInstant)
34108     {
34109         if ( !items || !items.length ) {
34110             return;
34111         }
34112         
34113         var standard = [
34114             ['xs', 'xs', 'xs', 'tall'],
34115             ['xs', 'xs', 'tall'],
34116             ['xs', 'xs', 'sm'],
34117             ['xs', 'xs', 'xs'],
34118             ['xs', 'tall'],
34119             ['xs', 'sm'],
34120             ['xs', 'xs'],
34121             ['xs'],
34122             
34123             ['sm', 'xs', 'xs'],
34124             ['sm', 'xs'],
34125             ['sm'],
34126             
34127             ['tall', 'xs', 'xs', 'xs'],
34128             ['tall', 'xs', 'xs'],
34129             ['tall', 'xs'],
34130             ['tall']
34131             
34132         ];
34133         
34134         var queue = [];
34135         
34136         var boxes = [];
34137         
34138         var box = [];
34139         
34140         Roo.each(items, function(item, k){
34141             
34142             switch (item.size) {
34143                 // these layouts take up a full box,
34144                 case 'md' :
34145                 case 'md-left' :
34146                 case 'md-right' :
34147                 case 'wide' :
34148                     
34149                     if(box.length){
34150                         boxes.push(box);
34151                         box = [];
34152                     }
34153                     
34154                     boxes.push([item]);
34155                     
34156                     break;
34157                     
34158                 case 'xs' :
34159                 case 'sm' :
34160                 case 'tall' :
34161                     
34162                     box.push(item);
34163                     
34164                     break;
34165                 default :
34166                     break;
34167                     
34168             }
34169             
34170         }, this);
34171         
34172         if(box.length){
34173             boxes.push(box);
34174             box = [];
34175         }
34176         
34177         var filterPattern = function(box, length)
34178         {
34179             if(!box.length){
34180                 return;
34181             }
34182             
34183             var match = false;
34184             
34185             var pattern = box.slice(0, length);
34186             
34187             var format = [];
34188             
34189             Roo.each(pattern, function(i){
34190                 format.push(i.size);
34191             }, this);
34192             
34193             Roo.each(standard, function(s){
34194                 
34195                 if(String(s) != String(format)){
34196                     return;
34197                 }
34198                 
34199                 match = true;
34200                 return false;
34201                 
34202             }, this);
34203             
34204             if(!match && length == 1){
34205                 return;
34206             }
34207             
34208             if(!match){
34209                 filterPattern(box, length - 1);
34210                 return;
34211             }
34212                 
34213             queue.push(pattern);
34214
34215             box = box.slice(length, box.length);
34216
34217             filterPattern(box, 4);
34218
34219             return;
34220             
34221         }
34222         
34223         Roo.each(boxes, function(box, k){
34224             
34225             if(!box.length){
34226                 return;
34227             }
34228             
34229             if(box.length == 1){
34230                 queue.push(box);
34231                 return;
34232             }
34233             
34234             filterPattern(box, 4);
34235             
34236         }, this);
34237         
34238         this._processVerticalLayoutQueue( queue, isInstant );
34239         
34240     },
34241     
34242 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34243 //    {
34244 //        if ( !items || !items.length ) {
34245 //            return;
34246 //        }
34247 //
34248 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34249 //        
34250 //    },
34251     
34252     _horizontalLayoutItems : function ( items , isInstant)
34253     {
34254         if ( !items || !items.length || items.length < 3) {
34255             return;
34256         }
34257         
34258         items.reverse();
34259         
34260         var eItems = items.slice(0, 3);
34261         
34262         items = items.slice(3, items.length);
34263         
34264         var standard = [
34265             ['xs', 'xs', 'xs', 'wide'],
34266             ['xs', 'xs', 'wide'],
34267             ['xs', 'xs', 'sm'],
34268             ['xs', 'xs', 'xs'],
34269             ['xs', 'wide'],
34270             ['xs', 'sm'],
34271             ['xs', 'xs'],
34272             ['xs'],
34273             
34274             ['sm', 'xs', 'xs'],
34275             ['sm', 'xs'],
34276             ['sm'],
34277             
34278             ['wide', 'xs', 'xs', 'xs'],
34279             ['wide', 'xs', 'xs'],
34280             ['wide', 'xs'],
34281             ['wide'],
34282             
34283             ['wide-thin']
34284         ];
34285         
34286         var queue = [];
34287         
34288         var boxes = [];
34289         
34290         var box = [];
34291         
34292         Roo.each(items, function(item, k){
34293             
34294             switch (item.size) {
34295                 case 'md' :
34296                 case 'md-left' :
34297                 case 'md-right' :
34298                 case 'tall' :
34299                     
34300                     if(box.length){
34301                         boxes.push(box);
34302                         box = [];
34303                     }
34304                     
34305                     boxes.push([item]);
34306                     
34307                     break;
34308                     
34309                 case 'xs' :
34310                 case 'sm' :
34311                 case 'wide' :
34312                 case 'wide-thin' :
34313                     
34314                     box.push(item);
34315                     
34316                     break;
34317                 default :
34318                     break;
34319                     
34320             }
34321             
34322         }, this);
34323         
34324         if(box.length){
34325             boxes.push(box);
34326             box = [];
34327         }
34328         
34329         var filterPattern = function(box, length)
34330         {
34331             if(!box.length){
34332                 return;
34333             }
34334             
34335             var match = false;
34336             
34337             var pattern = box.slice(0, length);
34338             
34339             var format = [];
34340             
34341             Roo.each(pattern, function(i){
34342                 format.push(i.size);
34343             }, this);
34344             
34345             Roo.each(standard, function(s){
34346                 
34347                 if(String(s) != String(format)){
34348                     return;
34349                 }
34350                 
34351                 match = true;
34352                 return false;
34353                 
34354             }, this);
34355             
34356             if(!match && length == 1){
34357                 return;
34358             }
34359             
34360             if(!match){
34361                 filterPattern(box, length - 1);
34362                 return;
34363             }
34364                 
34365             queue.push(pattern);
34366
34367             box = box.slice(length, box.length);
34368
34369             filterPattern(box, 4);
34370
34371             return;
34372             
34373         }
34374         
34375         Roo.each(boxes, function(box, k){
34376             
34377             if(!box.length){
34378                 return;
34379             }
34380             
34381             if(box.length == 1){
34382                 queue.push(box);
34383                 return;
34384             }
34385             
34386             filterPattern(box, 4);
34387             
34388         }, this);
34389         
34390         
34391         var prune = [];
34392         
34393         var pos = this.el.getBox(true);
34394         
34395         var minX = pos.x;
34396         
34397         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34398         
34399         var hit_end = false;
34400         
34401         Roo.each(queue, function(box){
34402             
34403             if(hit_end){
34404                 
34405                 Roo.each(box, function(b){
34406                 
34407                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34408                     b.el.hide();
34409
34410                 }, this);
34411
34412                 return;
34413             }
34414             
34415             var mx = 0;
34416             
34417             Roo.each(box, function(b){
34418                 
34419                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34420                 b.el.show();
34421
34422                 mx = Math.max(mx, b.x);
34423                 
34424             }, this);
34425             
34426             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34427             
34428             if(maxX < minX){
34429                 
34430                 Roo.each(box, function(b){
34431                 
34432                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34433                     b.el.hide();
34434                     
34435                 }, this);
34436                 
34437                 hit_end = true;
34438                 
34439                 return;
34440             }
34441             
34442             prune.push(box);
34443             
34444         }, this);
34445         
34446         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34447     },
34448     
34449     /** Sets position of item in DOM
34450     * @param {Element} item
34451     * @param {Number} x - horizontal position
34452     * @param {Number} y - vertical position
34453     * @param {Boolean} isInstant - disables transitions
34454     */
34455     _processVerticalLayoutQueue : function( queue, isInstant )
34456     {
34457         var pos = this.el.getBox(true);
34458         var x = pos.x;
34459         var y = pos.y;
34460         var maxY = [];
34461         
34462         for (var i = 0; i < this.cols; i++){
34463             maxY[i] = pos.y;
34464         }
34465         
34466         Roo.each(queue, function(box, k){
34467             
34468             var col = k % this.cols;
34469             
34470             Roo.each(box, function(b,kk){
34471                 
34472                 b.el.position('absolute');
34473                 
34474                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34475                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34476                 
34477                 if(b.size == 'md-left' || b.size == 'md-right'){
34478                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34479                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34480                 }
34481                 
34482                 b.el.setWidth(width);
34483                 b.el.setHeight(height);
34484                 // iframe?
34485                 b.el.select('iframe',true).setSize(width,height);
34486                 
34487             }, this);
34488             
34489             for (var i = 0; i < this.cols; i++){
34490                 
34491                 if(maxY[i] < maxY[col]){
34492                     col = i;
34493                     continue;
34494                 }
34495                 
34496                 col = Math.min(col, i);
34497                 
34498             }
34499             
34500             x = pos.x + col * (this.colWidth + this.padWidth);
34501             
34502             y = maxY[col];
34503             
34504             var positions = [];
34505             
34506             switch (box.length){
34507                 case 1 :
34508                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34509                     break;
34510                 case 2 :
34511                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34512                     break;
34513                 case 3 :
34514                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34515                     break;
34516                 case 4 :
34517                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34518                     break;
34519                 default :
34520                     break;
34521             }
34522             
34523             Roo.each(box, function(b,kk){
34524                 
34525                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34526                 
34527                 var sz = b.el.getSize();
34528                 
34529                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34530                 
34531             }, this);
34532             
34533         }, this);
34534         
34535         var mY = 0;
34536         
34537         for (var i = 0; i < this.cols; i++){
34538             mY = Math.max(mY, maxY[i]);
34539         }
34540         
34541         this.el.setHeight(mY - pos.y);
34542         
34543     },
34544     
34545 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34546 //    {
34547 //        var pos = this.el.getBox(true);
34548 //        var x = pos.x;
34549 //        var y = pos.y;
34550 //        var maxX = pos.right;
34551 //        
34552 //        var maxHeight = 0;
34553 //        
34554 //        Roo.each(items, function(item, k){
34555 //            
34556 //            var c = k % 2;
34557 //            
34558 //            item.el.position('absolute');
34559 //                
34560 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34561 //
34562 //            item.el.setWidth(width);
34563 //
34564 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34565 //
34566 //            item.el.setHeight(height);
34567 //            
34568 //            if(c == 0){
34569 //                item.el.setXY([x, y], isInstant ? false : true);
34570 //            } else {
34571 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34572 //            }
34573 //            
34574 //            y = y + height + this.alternativePadWidth;
34575 //            
34576 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34577 //            
34578 //        }, this);
34579 //        
34580 //        this.el.setHeight(maxHeight);
34581 //        
34582 //    },
34583     
34584     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34585     {
34586         var pos = this.el.getBox(true);
34587         
34588         var minX = pos.x;
34589         var minY = pos.y;
34590         
34591         var maxX = pos.right;
34592         
34593         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34594         
34595         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34596         
34597         Roo.each(queue, function(box, k){
34598             
34599             Roo.each(box, function(b, kk){
34600                 
34601                 b.el.position('absolute');
34602                 
34603                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34604                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34605                 
34606                 if(b.size == 'md-left' || b.size == 'md-right'){
34607                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34608                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34609                 }
34610                 
34611                 b.el.setWidth(width);
34612                 b.el.setHeight(height);
34613                 
34614             }, this);
34615             
34616             if(!box.length){
34617                 return;
34618             }
34619             
34620             var positions = [];
34621             
34622             switch (box.length){
34623                 case 1 :
34624                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34625                     break;
34626                 case 2 :
34627                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34628                     break;
34629                 case 3 :
34630                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34631                     break;
34632                 case 4 :
34633                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34634                     break;
34635                 default :
34636                     break;
34637             }
34638             
34639             Roo.each(box, function(b,kk){
34640                 
34641                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34642                 
34643                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34644                 
34645             }, this);
34646             
34647         }, this);
34648         
34649     },
34650     
34651     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34652     {
34653         Roo.each(eItems, function(b,k){
34654             
34655             b.size = (k == 0) ? 'sm' : 'xs';
34656             b.x = (k == 0) ? 2 : 1;
34657             b.y = (k == 0) ? 2 : 1;
34658             
34659             b.el.position('absolute');
34660             
34661             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34662                 
34663             b.el.setWidth(width);
34664             
34665             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34666             
34667             b.el.setHeight(height);
34668             
34669         }, this);
34670
34671         var positions = [];
34672         
34673         positions.push({
34674             x : maxX - this.unitWidth * 2 - this.gutter,
34675             y : minY
34676         });
34677         
34678         positions.push({
34679             x : maxX - this.unitWidth,
34680             y : minY + (this.unitWidth + this.gutter) * 2
34681         });
34682         
34683         positions.push({
34684             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34685             y : minY
34686         });
34687         
34688         Roo.each(eItems, function(b,k){
34689             
34690             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34691
34692         }, this);
34693         
34694     },
34695     
34696     getVerticalOneBoxColPositions : function(x, y, box)
34697     {
34698         var pos = [];
34699         
34700         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34701         
34702         if(box[0].size == 'md-left'){
34703             rand = 0;
34704         }
34705         
34706         if(box[0].size == 'md-right'){
34707             rand = 1;
34708         }
34709         
34710         pos.push({
34711             x : x + (this.unitWidth + this.gutter) * rand,
34712             y : y
34713         });
34714         
34715         return pos;
34716     },
34717     
34718     getVerticalTwoBoxColPositions : function(x, y, box)
34719     {
34720         var pos = [];
34721         
34722         if(box[0].size == 'xs'){
34723             
34724             pos.push({
34725                 x : x,
34726                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34727             });
34728
34729             pos.push({
34730                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34731                 y : y
34732             });
34733             
34734             return pos;
34735             
34736         }
34737         
34738         pos.push({
34739             x : x,
34740             y : y
34741         });
34742
34743         pos.push({
34744             x : x + (this.unitWidth + this.gutter) * 2,
34745             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34746         });
34747         
34748         return pos;
34749         
34750     },
34751     
34752     getVerticalThreeBoxColPositions : function(x, y, box)
34753     {
34754         var pos = [];
34755         
34756         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34757             
34758             pos.push({
34759                 x : x,
34760                 y : y
34761             });
34762
34763             pos.push({
34764                 x : x + (this.unitWidth + this.gutter) * 1,
34765                 y : y
34766             });
34767             
34768             pos.push({
34769                 x : x + (this.unitWidth + this.gutter) * 2,
34770                 y : y
34771             });
34772             
34773             return pos;
34774             
34775         }
34776         
34777         if(box[0].size == 'xs' && box[1].size == 'xs'){
34778             
34779             pos.push({
34780                 x : x,
34781                 y : y
34782             });
34783
34784             pos.push({
34785                 x : x,
34786                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34787             });
34788             
34789             pos.push({
34790                 x : x + (this.unitWidth + this.gutter) * 1,
34791                 y : y
34792             });
34793             
34794             return pos;
34795             
34796         }
34797         
34798         pos.push({
34799             x : x,
34800             y : y
34801         });
34802
34803         pos.push({
34804             x : x + (this.unitWidth + this.gutter) * 2,
34805             y : y
34806         });
34807
34808         pos.push({
34809             x : x + (this.unitWidth + this.gutter) * 2,
34810             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34811         });
34812             
34813         return pos;
34814         
34815     },
34816     
34817     getVerticalFourBoxColPositions : function(x, y, box)
34818     {
34819         var pos = [];
34820         
34821         if(box[0].size == 'xs'){
34822             
34823             pos.push({
34824                 x : x,
34825                 y : y
34826             });
34827
34828             pos.push({
34829                 x : x,
34830                 y : y + (this.unitHeight + this.gutter) * 1
34831             });
34832             
34833             pos.push({
34834                 x : x,
34835                 y : y + (this.unitHeight + this.gutter) * 2
34836             });
34837             
34838             pos.push({
34839                 x : x + (this.unitWidth + this.gutter) * 1,
34840                 y : y
34841             });
34842             
34843             return pos;
34844             
34845         }
34846         
34847         pos.push({
34848             x : x,
34849             y : y
34850         });
34851
34852         pos.push({
34853             x : x + (this.unitWidth + this.gutter) * 2,
34854             y : y
34855         });
34856
34857         pos.push({
34858             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34859             y : y + (this.unitHeight + this.gutter) * 1
34860         });
34861
34862         pos.push({
34863             x : x + (this.unitWidth + this.gutter) * 2,
34864             y : y + (this.unitWidth + this.gutter) * 2
34865         });
34866
34867         return pos;
34868         
34869     },
34870     
34871     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34872     {
34873         var pos = [];
34874         
34875         if(box[0].size == 'md-left'){
34876             pos.push({
34877                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34878                 y : minY
34879             });
34880             
34881             return pos;
34882         }
34883         
34884         if(box[0].size == 'md-right'){
34885             pos.push({
34886                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34887                 y : minY + (this.unitWidth + this.gutter) * 1
34888             });
34889             
34890             return pos;
34891         }
34892         
34893         var rand = Math.floor(Math.random() * (4 - box[0].y));
34894         
34895         pos.push({
34896             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34897             y : minY + (this.unitWidth + this.gutter) * rand
34898         });
34899         
34900         return pos;
34901         
34902     },
34903     
34904     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34905     {
34906         var pos = [];
34907         
34908         if(box[0].size == 'xs'){
34909             
34910             pos.push({
34911                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34912                 y : minY
34913             });
34914
34915             pos.push({
34916                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34917                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34918             });
34919             
34920             return pos;
34921             
34922         }
34923         
34924         pos.push({
34925             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34926             y : minY
34927         });
34928
34929         pos.push({
34930             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34931             y : minY + (this.unitWidth + this.gutter) * 2
34932         });
34933         
34934         return pos;
34935         
34936     },
34937     
34938     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34939     {
34940         var pos = [];
34941         
34942         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34943             
34944             pos.push({
34945                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34946                 y : minY
34947             });
34948
34949             pos.push({
34950                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34951                 y : minY + (this.unitWidth + this.gutter) * 1
34952             });
34953             
34954             pos.push({
34955                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34956                 y : minY + (this.unitWidth + this.gutter) * 2
34957             });
34958             
34959             return pos;
34960             
34961         }
34962         
34963         if(box[0].size == 'xs' && box[1].size == 'xs'){
34964             
34965             pos.push({
34966                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34967                 y : minY
34968             });
34969
34970             pos.push({
34971                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34972                 y : minY
34973             });
34974             
34975             pos.push({
34976                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34977                 y : minY + (this.unitWidth + this.gutter) * 1
34978             });
34979             
34980             return pos;
34981             
34982         }
34983         
34984         pos.push({
34985             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986             y : minY
34987         });
34988
34989         pos.push({
34990             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34991             y : minY + (this.unitWidth + this.gutter) * 2
34992         });
34993
34994         pos.push({
34995             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34996             y : minY + (this.unitWidth + this.gutter) * 2
34997         });
34998             
34999         return pos;
35000         
35001     },
35002     
35003     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35004     {
35005         var pos = [];
35006         
35007         if(box[0].size == 'xs'){
35008             
35009             pos.push({
35010                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35011                 y : minY
35012             });
35013
35014             pos.push({
35015                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35016                 y : minY
35017             });
35018             
35019             pos.push({
35020                 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),
35021                 y : minY
35022             });
35023             
35024             pos.push({
35025                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35026                 y : minY + (this.unitWidth + this.gutter) * 1
35027             });
35028             
35029             return pos;
35030             
35031         }
35032         
35033         pos.push({
35034             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35035             y : minY
35036         });
35037         
35038         pos.push({
35039             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35040             y : minY + (this.unitWidth + this.gutter) * 2
35041         });
35042         
35043         pos.push({
35044             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35045             y : minY + (this.unitWidth + this.gutter) * 2
35046         });
35047         
35048         pos.push({
35049             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),
35050             y : minY + (this.unitWidth + this.gutter) * 2
35051         });
35052
35053         return pos;
35054         
35055     },
35056     
35057     /**
35058     * remove a Masonry Brick
35059     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35060     */
35061     removeBrick : function(brick_id)
35062     {
35063         if (!brick_id) {
35064             return;
35065         }
35066         
35067         for (var i = 0; i<this.bricks.length; i++) {
35068             if (this.bricks[i].id == brick_id) {
35069                 this.bricks.splice(i,1);
35070                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35071                 this.initial();
35072             }
35073         }
35074     },
35075     
35076     /**
35077     * adds a Masonry Brick
35078     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35079     */
35080     addBrick : function(cfg)
35081     {
35082         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35083         //this.register(cn);
35084         cn.parentId = this.id;
35085         cn.render(this.el);
35086         return cn;
35087     },
35088     
35089     /**
35090     * register a Masonry Brick
35091     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35092     */
35093     
35094     register : function(brick)
35095     {
35096         this.bricks.push(brick);
35097         brick.masonryId = this.id;
35098     },
35099     
35100     /**
35101     * clear all the Masonry Brick
35102     */
35103     clearAll : function()
35104     {
35105         this.bricks = [];
35106         //this.getChildContainer().dom.innerHTML = "";
35107         this.el.dom.innerHTML = '';
35108     },
35109     
35110     getSelected : function()
35111     {
35112         if (!this.selectedBrick) {
35113             return false;
35114         }
35115         
35116         return this.selectedBrick;
35117     }
35118 });
35119
35120 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35121     
35122     groups: {},
35123      /**
35124     * register a Masonry Layout
35125     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35126     */
35127     
35128     register : function(layout)
35129     {
35130         this.groups[layout.id] = layout;
35131     },
35132     /**
35133     * fetch a  Masonry Layout based on the masonry layout ID
35134     * @param {string} the masonry layout to add
35135     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35136     */
35137     
35138     get: function(layout_id) {
35139         if (typeof(this.groups[layout_id]) == 'undefined') {
35140             return false;
35141         }
35142         return this.groups[layout_id] ;
35143     }
35144     
35145     
35146     
35147 });
35148
35149  
35150
35151  /**
35152  *
35153  * This is based on 
35154  * http://masonry.desandro.com
35155  *
35156  * The idea is to render all the bricks based on vertical width...
35157  *
35158  * The original code extends 'outlayer' - we might need to use that....
35159  * 
35160  */
35161
35162
35163 /**
35164  * @class Roo.bootstrap.LayoutMasonryAuto
35165  * @extends Roo.bootstrap.Component
35166  * Bootstrap Layout Masonry class
35167  * 
35168  * @constructor
35169  * Create a new Element
35170  * @param {Object} config The config object
35171  */
35172
35173 Roo.bootstrap.LayoutMasonryAuto = function(config){
35174     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35175 };
35176
35177 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35178     
35179       /**
35180      * @cfg {Boolean} isFitWidth  - resize the width..
35181      */   
35182     isFitWidth : false,  // options..
35183     /**
35184      * @cfg {Boolean} isOriginLeft = left align?
35185      */   
35186     isOriginLeft : true,
35187     /**
35188      * @cfg {Boolean} isOriginTop = top align?
35189      */   
35190     isOriginTop : false,
35191     /**
35192      * @cfg {Boolean} isLayoutInstant = no animation?
35193      */   
35194     isLayoutInstant : false, // needed?
35195     /**
35196      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35197      */   
35198     isResizingContainer : true,
35199     /**
35200      * @cfg {Number} columnWidth  width of the columns 
35201      */   
35202     
35203     columnWidth : 0,
35204     
35205     /**
35206      * @cfg {Number} maxCols maximum number of columns
35207      */   
35208     
35209     maxCols: 0,
35210     /**
35211      * @cfg {Number} padHeight padding below box..
35212      */   
35213     
35214     padHeight : 10, 
35215     
35216     /**
35217      * @cfg {Boolean} isAutoInitial defalut true
35218      */   
35219     
35220     isAutoInitial : true, 
35221     
35222     // private?
35223     gutter : 0,
35224     
35225     containerWidth: 0,
35226     initialColumnWidth : 0,
35227     currentSize : null,
35228     
35229     colYs : null, // array.
35230     maxY : 0,
35231     padWidth: 10,
35232     
35233     
35234     tag: 'div',
35235     cls: '',
35236     bricks: null, //CompositeElement
35237     cols : 0, // array?
35238     // element : null, // wrapped now this.el
35239     _isLayoutInited : null, 
35240     
35241     
35242     getAutoCreate : function(){
35243         
35244         var cfg = {
35245             tag: this.tag,
35246             cls: 'blog-masonary-wrapper ' + this.cls,
35247             cn : {
35248                 cls : 'mas-boxes masonary'
35249             }
35250         };
35251         
35252         return cfg;
35253     },
35254     
35255     getChildContainer: function( )
35256     {
35257         if (this.boxesEl) {
35258             return this.boxesEl;
35259         }
35260         
35261         this.boxesEl = this.el.select('.mas-boxes').first();
35262         
35263         return this.boxesEl;
35264     },
35265     
35266     
35267     initEvents : function()
35268     {
35269         var _this = this;
35270         
35271         if(this.isAutoInitial){
35272             Roo.log('hook children rendered');
35273             this.on('childrenrendered', function() {
35274                 Roo.log('children rendered');
35275                 _this.initial();
35276             } ,this);
35277         }
35278         
35279     },
35280     
35281     initial : function()
35282     {
35283         this.reloadItems();
35284
35285         this.currentSize = this.el.getBox(true);
35286
35287         /// was window resize... - let's see if this works..
35288         Roo.EventManager.onWindowResize(this.resize, this); 
35289
35290         if(!this.isAutoInitial){
35291             this.layout();
35292             return;
35293         }
35294         
35295         this.layout.defer(500,this);
35296     },
35297     
35298     reloadItems: function()
35299     {
35300         this.bricks = this.el.select('.masonry-brick', true);
35301         
35302         this.bricks.each(function(b) {
35303             //Roo.log(b.getSize());
35304             if (!b.attr('originalwidth')) {
35305                 b.attr('originalwidth',  b.getSize().width);
35306             }
35307             
35308         });
35309         
35310         Roo.log(this.bricks.elements.length);
35311     },
35312     
35313     resize : function()
35314     {
35315         Roo.log('resize');
35316         var cs = this.el.getBox(true);
35317         
35318         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35319             Roo.log("no change in with or X");
35320             return;
35321         }
35322         this.currentSize = cs;
35323         this.layout();
35324     },
35325     
35326     layout : function()
35327     {
35328          Roo.log('layout');
35329         this._resetLayout();
35330         //this._manageStamps();
35331       
35332         // don't animate first layout
35333         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35334         this.layoutItems( isInstant );
35335       
35336         // flag for initalized
35337         this._isLayoutInited = true;
35338     },
35339     
35340     layoutItems : function( isInstant )
35341     {
35342         //var items = this._getItemsForLayout( this.items );
35343         // original code supports filtering layout items.. we just ignore it..
35344         
35345         this._layoutItems( this.bricks , isInstant );
35346       
35347         this._postLayout();
35348     },
35349     _layoutItems : function ( items , isInstant)
35350     {
35351        //this.fireEvent( 'layout', this, items );
35352     
35353
35354         if ( !items || !items.elements.length ) {
35355           // no items, emit event with empty array
35356             return;
35357         }
35358
35359         var queue = [];
35360         items.each(function(item) {
35361             Roo.log("layout item");
35362             Roo.log(item);
35363             // get x/y object from method
35364             var position = this._getItemLayoutPosition( item );
35365             // enqueue
35366             position.item = item;
35367             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35368             queue.push( position );
35369         }, this);
35370       
35371         this._processLayoutQueue( queue );
35372     },
35373     /** Sets position of item in DOM
35374     * @param {Element} item
35375     * @param {Number} x - horizontal position
35376     * @param {Number} y - vertical position
35377     * @param {Boolean} isInstant - disables transitions
35378     */
35379     _processLayoutQueue : function( queue )
35380     {
35381         for ( var i=0, len = queue.length; i < len; i++ ) {
35382             var obj = queue[i];
35383             obj.item.position('absolute');
35384             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35385         }
35386     },
35387       
35388     
35389     /**
35390     * Any logic you want to do after each layout,
35391     * i.e. size the container
35392     */
35393     _postLayout : function()
35394     {
35395         this.resizeContainer();
35396     },
35397     
35398     resizeContainer : function()
35399     {
35400         if ( !this.isResizingContainer ) {
35401             return;
35402         }
35403         var size = this._getContainerSize();
35404         if ( size ) {
35405             this.el.setSize(size.width,size.height);
35406             this.boxesEl.setSize(size.width,size.height);
35407         }
35408     },
35409     
35410     
35411     
35412     _resetLayout : function()
35413     {
35414         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35415         this.colWidth = this.el.getWidth();
35416         //this.gutter = this.el.getWidth(); 
35417         
35418         this.measureColumns();
35419
35420         // reset column Y
35421         var i = this.cols;
35422         this.colYs = [];
35423         while (i--) {
35424             this.colYs.push( 0 );
35425         }
35426     
35427         this.maxY = 0;
35428     },
35429
35430     measureColumns : function()
35431     {
35432         this.getContainerWidth();
35433       // if columnWidth is 0, default to outerWidth of first item
35434         if ( !this.columnWidth ) {
35435             var firstItem = this.bricks.first();
35436             Roo.log(firstItem);
35437             this.columnWidth  = this.containerWidth;
35438             if (firstItem && firstItem.attr('originalwidth') ) {
35439                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35440             }
35441             // columnWidth fall back to item of first element
35442             Roo.log("set column width?");
35443                         this.initialColumnWidth = this.columnWidth  ;
35444
35445             // if first elem has no width, default to size of container
35446             
35447         }
35448         
35449         
35450         if (this.initialColumnWidth) {
35451             this.columnWidth = this.initialColumnWidth;
35452         }
35453         
35454         
35455             
35456         // column width is fixed at the top - however if container width get's smaller we should
35457         // reduce it...
35458         
35459         // this bit calcs how man columns..
35460             
35461         var columnWidth = this.columnWidth += this.gutter;
35462       
35463         // calculate columns
35464         var containerWidth = this.containerWidth + this.gutter;
35465         
35466         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35467         // fix rounding errors, typically with gutters
35468         var excess = columnWidth - containerWidth % columnWidth;
35469         
35470         
35471         // if overshoot is less than a pixel, round up, otherwise floor it
35472         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35473         cols = Math[ mathMethod ]( cols );
35474         this.cols = Math.max( cols, 1 );
35475         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35476         
35477          // padding positioning..
35478         var totalColWidth = this.cols * this.columnWidth;
35479         var padavail = this.containerWidth - totalColWidth;
35480         // so for 2 columns - we need 3 'pads'
35481         
35482         var padNeeded = (1+this.cols) * this.padWidth;
35483         
35484         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35485         
35486         this.columnWidth += padExtra
35487         //this.padWidth = Math.floor(padavail /  ( this.cols));
35488         
35489         // adjust colum width so that padding is fixed??
35490         
35491         // we have 3 columns ... total = width * 3
35492         // we have X left over... that should be used by 
35493         
35494         //if (this.expandC) {
35495             
35496         //}
35497         
35498         
35499         
35500     },
35501     
35502     getContainerWidth : function()
35503     {
35504        /* // container is parent if fit width
35505         var container = this.isFitWidth ? this.element.parentNode : this.element;
35506         // check that this.size and size are there
35507         // IE8 triggers resize on body size change, so they might not be
35508         
35509         var size = getSize( container );  //FIXME
35510         this.containerWidth = size && size.innerWidth; //FIXME
35511         */
35512          
35513         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35514         
35515     },
35516     
35517     _getItemLayoutPosition : function( item )  // what is item?
35518     {
35519         // we resize the item to our columnWidth..
35520       
35521         item.setWidth(this.columnWidth);
35522         item.autoBoxAdjust  = false;
35523         
35524         var sz = item.getSize();
35525  
35526         // how many columns does this brick span
35527         var remainder = this.containerWidth % this.columnWidth;
35528         
35529         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35530         // round if off by 1 pixel, otherwise use ceil
35531         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35532         colSpan = Math.min( colSpan, this.cols );
35533         
35534         // normally this should be '1' as we dont' currently allow multi width columns..
35535         
35536         var colGroup = this._getColGroup( colSpan );
35537         // get the minimum Y value from the columns
35538         var minimumY = Math.min.apply( Math, colGroup );
35539         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35540         
35541         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35542          
35543         // position the brick
35544         var position = {
35545             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35546             y: this.currentSize.y + minimumY + this.padHeight
35547         };
35548         
35549         Roo.log(position);
35550         // apply setHeight to necessary columns
35551         var setHeight = minimumY + sz.height + this.padHeight;
35552         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35553         
35554         var setSpan = this.cols + 1 - colGroup.length;
35555         for ( var i = 0; i < setSpan; i++ ) {
35556           this.colYs[ shortColIndex + i ] = setHeight ;
35557         }
35558       
35559         return position;
35560     },
35561     
35562     /**
35563      * @param {Number} colSpan - number of columns the element spans
35564      * @returns {Array} colGroup
35565      */
35566     _getColGroup : function( colSpan )
35567     {
35568         if ( colSpan < 2 ) {
35569           // if brick spans only one column, use all the column Ys
35570           return this.colYs;
35571         }
35572       
35573         var colGroup = [];
35574         // how many different places could this brick fit horizontally
35575         var groupCount = this.cols + 1 - colSpan;
35576         // for each group potential horizontal position
35577         for ( var i = 0; i < groupCount; i++ ) {
35578           // make an array of colY values for that one group
35579           var groupColYs = this.colYs.slice( i, i + colSpan );
35580           // and get the max value of the array
35581           colGroup[i] = Math.max.apply( Math, groupColYs );
35582         }
35583         return colGroup;
35584     },
35585     /*
35586     _manageStamp : function( stamp )
35587     {
35588         var stampSize =  stamp.getSize();
35589         var offset = stamp.getBox();
35590         // get the columns that this stamp affects
35591         var firstX = this.isOriginLeft ? offset.x : offset.right;
35592         var lastX = firstX + stampSize.width;
35593         var firstCol = Math.floor( firstX / this.columnWidth );
35594         firstCol = Math.max( 0, firstCol );
35595         
35596         var lastCol = Math.floor( lastX / this.columnWidth );
35597         // lastCol should not go over if multiple of columnWidth #425
35598         lastCol -= lastX % this.columnWidth ? 0 : 1;
35599         lastCol = Math.min( this.cols - 1, lastCol );
35600         
35601         // set colYs to bottom of the stamp
35602         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35603             stampSize.height;
35604             
35605         for ( var i = firstCol; i <= lastCol; i++ ) {
35606           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35607         }
35608     },
35609     */
35610     
35611     _getContainerSize : function()
35612     {
35613         this.maxY = Math.max.apply( Math, this.colYs );
35614         var size = {
35615             height: this.maxY
35616         };
35617       
35618         if ( this.isFitWidth ) {
35619             size.width = this._getContainerFitWidth();
35620         }
35621       
35622         return size;
35623     },
35624     
35625     _getContainerFitWidth : function()
35626     {
35627         var unusedCols = 0;
35628         // count unused columns
35629         var i = this.cols;
35630         while ( --i ) {
35631           if ( this.colYs[i] !== 0 ) {
35632             break;
35633           }
35634           unusedCols++;
35635         }
35636         // fit container to columns that have been used
35637         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35638     },
35639     
35640     needsResizeLayout : function()
35641     {
35642         var previousWidth = this.containerWidth;
35643         this.getContainerWidth();
35644         return previousWidth !== this.containerWidth;
35645     }
35646  
35647 });
35648
35649  
35650
35651  /*
35652  * - LGPL
35653  *
35654  * element
35655  * 
35656  */
35657
35658 /**
35659  * @class Roo.bootstrap.MasonryBrick
35660  * @extends Roo.bootstrap.Component
35661  * Bootstrap MasonryBrick class
35662  * 
35663  * @constructor
35664  * Create a new MasonryBrick
35665  * @param {Object} config The config object
35666  */
35667
35668 Roo.bootstrap.MasonryBrick = function(config){
35669     
35670     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35671     
35672     Roo.bootstrap.MasonryBrick.register(this);
35673     
35674     this.addEvents({
35675         // raw events
35676         /**
35677          * @event click
35678          * When a MasonryBrick is clcik
35679          * @param {Roo.bootstrap.MasonryBrick} this
35680          * @param {Roo.EventObject} e
35681          */
35682         "click" : true
35683     });
35684 };
35685
35686 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35687     
35688     /**
35689      * @cfg {String} title
35690      */   
35691     title : '',
35692     /**
35693      * @cfg {String} html
35694      */   
35695     html : '',
35696     /**
35697      * @cfg {String} bgimage
35698      */   
35699     bgimage : '',
35700     /**
35701      * @cfg {String} videourl
35702      */   
35703     videourl : '',
35704     /**
35705      * @cfg {String} cls
35706      */   
35707     cls : '',
35708     /**
35709      * @cfg {String} href
35710      */   
35711     href : '',
35712     /**
35713      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35714      */   
35715     size : 'xs',
35716     
35717     /**
35718      * @cfg {String} placetitle (center|bottom)
35719      */   
35720     placetitle : '',
35721     
35722     /**
35723      * @cfg {Boolean} isFitContainer defalut true
35724      */   
35725     isFitContainer : true, 
35726     
35727     /**
35728      * @cfg {Boolean} preventDefault defalut false
35729      */   
35730     preventDefault : false, 
35731     
35732     /**
35733      * @cfg {Boolean} inverse defalut false
35734      */   
35735     maskInverse : false, 
35736     
35737     getAutoCreate : function()
35738     {
35739         if(!this.isFitContainer){
35740             return this.getSplitAutoCreate();
35741         }
35742         
35743         var cls = 'masonry-brick masonry-brick-full';
35744         
35745         if(this.href.length){
35746             cls += ' masonry-brick-link';
35747         }
35748         
35749         if(this.bgimage.length){
35750             cls += ' masonry-brick-image';
35751         }
35752         
35753         if(this.maskInverse){
35754             cls += ' mask-inverse';
35755         }
35756         
35757         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35758             cls += ' enable-mask';
35759         }
35760         
35761         if(this.size){
35762             cls += ' masonry-' + this.size + '-brick';
35763         }
35764         
35765         if(this.placetitle.length){
35766             
35767             switch (this.placetitle) {
35768                 case 'center' :
35769                     cls += ' masonry-center-title';
35770                     break;
35771                 case 'bottom' :
35772                     cls += ' masonry-bottom-title';
35773                     break;
35774                 default:
35775                     break;
35776             }
35777             
35778         } else {
35779             if(!this.html.length && !this.bgimage.length){
35780                 cls += ' masonry-center-title';
35781             }
35782
35783             if(!this.html.length && this.bgimage.length){
35784                 cls += ' masonry-bottom-title';
35785             }
35786         }
35787         
35788         if(this.cls){
35789             cls += ' ' + this.cls;
35790         }
35791         
35792         var cfg = {
35793             tag: (this.href.length) ? 'a' : 'div',
35794             cls: cls,
35795             cn: [
35796                 {
35797                     tag: 'div',
35798                     cls: 'masonry-brick-mask'
35799                 },
35800                 {
35801                     tag: 'div',
35802                     cls: 'masonry-brick-paragraph',
35803                     cn: []
35804                 }
35805             ]
35806         };
35807         
35808         if(this.href.length){
35809             cfg.href = this.href;
35810         }
35811         
35812         var cn = cfg.cn[1].cn;
35813         
35814         if(this.title.length){
35815             cn.push({
35816                 tag: 'h4',
35817                 cls: 'masonry-brick-title',
35818                 html: this.title
35819             });
35820         }
35821         
35822         if(this.html.length){
35823             cn.push({
35824                 tag: 'p',
35825                 cls: 'masonry-brick-text',
35826                 html: this.html
35827             });
35828         }
35829         
35830         if (!this.title.length && !this.html.length) {
35831             cfg.cn[1].cls += ' hide';
35832         }
35833         
35834         if(this.bgimage.length){
35835             cfg.cn.push({
35836                 tag: 'img',
35837                 cls: 'masonry-brick-image-view',
35838                 src: this.bgimage
35839             });
35840         }
35841         
35842         if(this.videourl.length){
35843             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35844             // youtube support only?
35845             cfg.cn.push({
35846                 tag: 'iframe',
35847                 cls: 'masonry-brick-image-view',
35848                 src: vurl,
35849                 frameborder : 0,
35850                 allowfullscreen : true
35851             });
35852         }
35853         
35854         return cfg;
35855         
35856     },
35857     
35858     getSplitAutoCreate : function()
35859     {
35860         var cls = 'masonry-brick masonry-brick-split';
35861         
35862         if(this.href.length){
35863             cls += ' masonry-brick-link';
35864         }
35865         
35866         if(this.bgimage.length){
35867             cls += ' masonry-brick-image';
35868         }
35869         
35870         if(this.size){
35871             cls += ' masonry-' + this.size + '-brick';
35872         }
35873         
35874         switch (this.placetitle) {
35875             case 'center' :
35876                 cls += ' masonry-center-title';
35877                 break;
35878             case 'bottom' :
35879                 cls += ' masonry-bottom-title';
35880                 break;
35881             default:
35882                 if(!this.bgimage.length){
35883                     cls += ' masonry-center-title';
35884                 }
35885
35886                 if(this.bgimage.length){
35887                     cls += ' masonry-bottom-title';
35888                 }
35889                 break;
35890         }
35891         
35892         if(this.cls){
35893             cls += ' ' + this.cls;
35894         }
35895         
35896         var cfg = {
35897             tag: (this.href.length) ? 'a' : 'div',
35898             cls: cls,
35899             cn: [
35900                 {
35901                     tag: 'div',
35902                     cls: 'masonry-brick-split-head',
35903                     cn: [
35904                         {
35905                             tag: 'div',
35906                             cls: 'masonry-brick-paragraph',
35907                             cn: []
35908                         }
35909                     ]
35910                 },
35911                 {
35912                     tag: 'div',
35913                     cls: 'masonry-brick-split-body',
35914                     cn: []
35915                 }
35916             ]
35917         };
35918         
35919         if(this.href.length){
35920             cfg.href = this.href;
35921         }
35922         
35923         if(this.title.length){
35924             cfg.cn[0].cn[0].cn.push({
35925                 tag: 'h4',
35926                 cls: 'masonry-brick-title',
35927                 html: this.title
35928             });
35929         }
35930         
35931         if(this.html.length){
35932             cfg.cn[1].cn.push({
35933                 tag: 'p',
35934                 cls: 'masonry-brick-text',
35935                 html: this.html
35936             });
35937         }
35938
35939         if(this.bgimage.length){
35940             cfg.cn[0].cn.push({
35941                 tag: 'img',
35942                 cls: 'masonry-brick-image-view',
35943                 src: this.bgimage
35944             });
35945         }
35946         
35947         if(this.videourl.length){
35948             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35949             // youtube support only?
35950             cfg.cn[0].cn.cn.push({
35951                 tag: 'iframe',
35952                 cls: 'masonry-brick-image-view',
35953                 src: vurl,
35954                 frameborder : 0,
35955                 allowfullscreen : true
35956             });
35957         }
35958         
35959         return cfg;
35960     },
35961     
35962     initEvents: function() 
35963     {
35964         switch (this.size) {
35965             case 'xs' :
35966                 this.x = 1;
35967                 this.y = 1;
35968                 break;
35969             case 'sm' :
35970                 this.x = 2;
35971                 this.y = 2;
35972                 break;
35973             case 'md' :
35974             case 'md-left' :
35975             case 'md-right' :
35976                 this.x = 3;
35977                 this.y = 3;
35978                 break;
35979             case 'tall' :
35980                 this.x = 2;
35981                 this.y = 3;
35982                 break;
35983             case 'wide' :
35984                 this.x = 3;
35985                 this.y = 2;
35986                 break;
35987             case 'wide-thin' :
35988                 this.x = 3;
35989                 this.y = 1;
35990                 break;
35991                         
35992             default :
35993                 break;
35994         }
35995         
35996         if(Roo.isTouch){
35997             this.el.on('touchstart', this.onTouchStart, this);
35998             this.el.on('touchmove', this.onTouchMove, this);
35999             this.el.on('touchend', this.onTouchEnd, this);
36000             this.el.on('contextmenu', this.onContextMenu, this);
36001         } else {
36002             this.el.on('mouseenter'  ,this.enter, this);
36003             this.el.on('mouseleave', this.leave, this);
36004             this.el.on('click', this.onClick, this);
36005         }
36006         
36007         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36008             this.parent().bricks.push(this);   
36009         }
36010         
36011     },
36012     
36013     onClick: function(e, el)
36014     {
36015         var time = this.endTimer - this.startTimer;
36016         // Roo.log(e.preventDefault());
36017         if(Roo.isTouch){
36018             if(time > 1000){
36019                 e.preventDefault();
36020                 return;
36021             }
36022         }
36023         
36024         if(!this.preventDefault){
36025             return;
36026         }
36027         
36028         e.preventDefault();
36029         
36030         if (this.activeClass != '') {
36031             this.selectBrick();
36032         }
36033         
36034         this.fireEvent('click', this, e);
36035     },
36036     
36037     enter: function(e, el)
36038     {
36039         e.preventDefault();
36040         
36041         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36042             return;
36043         }
36044         
36045         if(this.bgimage.length && this.html.length){
36046             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36047         }
36048     },
36049     
36050     leave: function(e, el)
36051     {
36052         e.preventDefault();
36053         
36054         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36055             return;
36056         }
36057         
36058         if(this.bgimage.length && this.html.length){
36059             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36060         }
36061     },
36062     
36063     onTouchStart: function(e, el)
36064     {
36065 //        e.preventDefault();
36066         
36067         this.touchmoved = false;
36068         
36069         if(!this.isFitContainer){
36070             return;
36071         }
36072         
36073         if(!this.bgimage.length || !this.html.length){
36074             return;
36075         }
36076         
36077         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36078         
36079         this.timer = new Date().getTime();
36080         
36081     },
36082     
36083     onTouchMove: function(e, el)
36084     {
36085         this.touchmoved = true;
36086     },
36087     
36088     onContextMenu : function(e,el)
36089     {
36090         e.preventDefault();
36091         e.stopPropagation();
36092         return false;
36093     },
36094     
36095     onTouchEnd: function(e, el)
36096     {
36097 //        e.preventDefault();
36098         
36099         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36100         
36101             this.leave(e,el);
36102             
36103             return;
36104         }
36105         
36106         if(!this.bgimage.length || !this.html.length){
36107             
36108             if(this.href.length){
36109                 window.location.href = this.href;
36110             }
36111             
36112             return;
36113         }
36114         
36115         if(!this.isFitContainer){
36116             return;
36117         }
36118         
36119         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36120         
36121         window.location.href = this.href;
36122     },
36123     
36124     //selection on single brick only
36125     selectBrick : function() {
36126         
36127         if (!this.parentId) {
36128             return;
36129         }
36130         
36131         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36132         var index = m.selectedBrick.indexOf(this.id);
36133         
36134         if ( index > -1) {
36135             m.selectedBrick.splice(index,1);
36136             this.el.removeClass(this.activeClass);
36137             return;
36138         }
36139         
36140         for(var i = 0; i < m.selectedBrick.length; i++) {
36141             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36142             b.el.removeClass(b.activeClass);
36143         }
36144         
36145         m.selectedBrick = [];
36146         
36147         m.selectedBrick.push(this.id);
36148         this.el.addClass(this.activeClass);
36149         return;
36150     },
36151     
36152     isSelected : function(){
36153         return this.el.hasClass(this.activeClass);
36154         
36155     }
36156 });
36157
36158 Roo.apply(Roo.bootstrap.MasonryBrick, {
36159     
36160     //groups: {},
36161     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36162      /**
36163     * register a Masonry Brick
36164     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36165     */
36166     
36167     register : function(brick)
36168     {
36169         //this.groups[brick.id] = brick;
36170         this.groups.add(brick.id, brick);
36171     },
36172     /**
36173     * fetch a  masonry brick based on the masonry brick ID
36174     * @param {string} the masonry brick to add
36175     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36176     */
36177     
36178     get: function(brick_id) 
36179     {
36180         // if (typeof(this.groups[brick_id]) == 'undefined') {
36181         //     return false;
36182         // }
36183         // return this.groups[brick_id] ;
36184         
36185         if(this.groups.key(brick_id)) {
36186             return this.groups.key(brick_id);
36187         }
36188         
36189         return false;
36190     }
36191     
36192     
36193     
36194 });
36195
36196  /*
36197  * - LGPL
36198  *
36199  * element
36200  * 
36201  */
36202
36203 /**
36204  * @class Roo.bootstrap.Brick
36205  * @extends Roo.bootstrap.Component
36206  * Bootstrap Brick class
36207  * 
36208  * @constructor
36209  * Create a new Brick
36210  * @param {Object} config The config object
36211  */
36212
36213 Roo.bootstrap.Brick = function(config){
36214     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36215     
36216     this.addEvents({
36217         // raw events
36218         /**
36219          * @event click
36220          * When a Brick is click
36221          * @param {Roo.bootstrap.Brick} this
36222          * @param {Roo.EventObject} e
36223          */
36224         "click" : true
36225     });
36226 };
36227
36228 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36229     
36230     /**
36231      * @cfg {String} title
36232      */   
36233     title : '',
36234     /**
36235      * @cfg {String} html
36236      */   
36237     html : '',
36238     /**
36239      * @cfg {String} bgimage
36240      */   
36241     bgimage : '',
36242     /**
36243      * @cfg {String} cls
36244      */   
36245     cls : '',
36246     /**
36247      * @cfg {String} href
36248      */   
36249     href : '',
36250     /**
36251      * @cfg {String} video
36252      */   
36253     video : '',
36254     /**
36255      * @cfg {Boolean} square
36256      */   
36257     square : true,
36258     
36259     getAutoCreate : function()
36260     {
36261         var cls = 'roo-brick';
36262         
36263         if(this.href.length){
36264             cls += ' roo-brick-link';
36265         }
36266         
36267         if(this.bgimage.length){
36268             cls += ' roo-brick-image';
36269         }
36270         
36271         if(!this.html.length && !this.bgimage.length){
36272             cls += ' roo-brick-center-title';
36273         }
36274         
36275         if(!this.html.length && this.bgimage.length){
36276             cls += ' roo-brick-bottom-title';
36277         }
36278         
36279         if(this.cls){
36280             cls += ' ' + this.cls;
36281         }
36282         
36283         var cfg = {
36284             tag: (this.href.length) ? 'a' : 'div',
36285             cls: cls,
36286             cn: [
36287                 {
36288                     tag: 'div',
36289                     cls: 'roo-brick-paragraph',
36290                     cn: []
36291                 }
36292             ]
36293         };
36294         
36295         if(this.href.length){
36296             cfg.href = this.href;
36297         }
36298         
36299         var cn = cfg.cn[0].cn;
36300         
36301         if(this.title.length){
36302             cn.push({
36303                 tag: 'h4',
36304                 cls: 'roo-brick-title',
36305                 html: this.title
36306             });
36307         }
36308         
36309         if(this.html.length){
36310             cn.push({
36311                 tag: 'p',
36312                 cls: 'roo-brick-text',
36313                 html: this.html
36314             });
36315         } else {
36316             cn.cls += ' hide';
36317         }
36318         
36319         if(this.bgimage.length){
36320             cfg.cn.push({
36321                 tag: 'img',
36322                 cls: 'roo-brick-image-view',
36323                 src: this.bgimage
36324             });
36325         }
36326         
36327         return cfg;
36328     },
36329     
36330     initEvents: function() 
36331     {
36332         if(this.title.length || this.html.length){
36333             this.el.on('mouseenter'  ,this.enter, this);
36334             this.el.on('mouseleave', this.leave, this);
36335         }
36336         
36337         Roo.EventManager.onWindowResize(this.resize, this); 
36338         
36339         if(this.bgimage.length){
36340             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36341             this.imageEl.on('load', this.onImageLoad, this);
36342             return;
36343         }
36344         
36345         this.resize();
36346     },
36347     
36348     onImageLoad : function()
36349     {
36350         this.resize();
36351     },
36352     
36353     resize : function()
36354     {
36355         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36356         
36357         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36358         
36359         if(this.bgimage.length){
36360             var image = this.el.select('.roo-brick-image-view', true).first();
36361             
36362             image.setWidth(paragraph.getWidth());
36363             
36364             if(this.square){
36365                 image.setHeight(paragraph.getWidth());
36366             }
36367             
36368             this.el.setHeight(image.getHeight());
36369             paragraph.setHeight(image.getHeight());
36370             
36371         }
36372         
36373     },
36374     
36375     enter: function(e, el)
36376     {
36377         e.preventDefault();
36378         
36379         if(this.bgimage.length){
36380             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36381             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36382         }
36383     },
36384     
36385     leave: function(e, el)
36386     {
36387         e.preventDefault();
36388         
36389         if(this.bgimage.length){
36390             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36391             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36392         }
36393     }
36394     
36395 });
36396
36397  
36398
36399  /*
36400  * - LGPL
36401  *
36402  * Number field 
36403  */
36404
36405 /**
36406  * @class Roo.bootstrap.NumberField
36407  * @extends Roo.bootstrap.Input
36408  * Bootstrap NumberField class
36409  * 
36410  * 
36411  * 
36412  * 
36413  * @constructor
36414  * Create a new NumberField
36415  * @param {Object} config The config object
36416  */
36417
36418 Roo.bootstrap.NumberField = function(config){
36419     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36420 };
36421
36422 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36423     
36424     /**
36425      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36426      */
36427     allowDecimals : true,
36428     /**
36429      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36430      */
36431     decimalSeparator : ".",
36432     /**
36433      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36434      */
36435     decimalPrecision : 2,
36436     /**
36437      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36438      */
36439     allowNegative : true,
36440     
36441     /**
36442      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36443      */
36444     allowZero: true,
36445     /**
36446      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36447      */
36448     minValue : Number.NEGATIVE_INFINITY,
36449     /**
36450      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36451      */
36452     maxValue : Number.MAX_VALUE,
36453     /**
36454      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36455      */
36456     minText : "The minimum value for this field is {0}",
36457     /**
36458      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36459      */
36460     maxText : "The maximum value for this field is {0}",
36461     /**
36462      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36463      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36464      */
36465     nanText : "{0} is not a valid number",
36466     /**
36467      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36468      */
36469     thousandsDelimiter : false,
36470     /**
36471      * @cfg {String} valueAlign alignment of value
36472      */
36473     valueAlign : "left",
36474
36475     getAutoCreate : function()
36476     {
36477         var hiddenInput = {
36478             tag: 'input',
36479             type: 'hidden',
36480             id: Roo.id(),
36481             cls: 'hidden-number-input'
36482         };
36483         
36484         if (this.name) {
36485             hiddenInput.name = this.name;
36486         }
36487         
36488         this.name = '';
36489         
36490         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36491         
36492         this.name = hiddenInput.name;
36493         
36494         if(cfg.cn.length > 0) {
36495             cfg.cn.push(hiddenInput);
36496         }
36497         
36498         return cfg;
36499     },
36500
36501     // private
36502     initEvents : function()
36503     {   
36504         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36505         
36506         var allowed = "0123456789";
36507         
36508         if(this.allowDecimals){
36509             allowed += this.decimalSeparator;
36510         }
36511         
36512         if(this.allowNegative){
36513             allowed += "-";
36514         }
36515         
36516         if(this.thousandsDelimiter) {
36517             allowed += ",";
36518         }
36519         
36520         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36521         
36522         var keyPress = function(e){
36523             
36524             var k = e.getKey();
36525             
36526             var c = e.getCharCode();
36527             
36528             if(
36529                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36530                     allowed.indexOf(String.fromCharCode(c)) === -1
36531             ){
36532                 e.stopEvent();
36533                 return;
36534             }
36535             
36536             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36537                 return;
36538             }
36539             
36540             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36541                 e.stopEvent();
36542             }
36543         };
36544         
36545         this.el.on("keypress", keyPress, this);
36546     },
36547     
36548     validateValue : function(value)
36549     {
36550         
36551         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36552             return false;
36553         }
36554         
36555         var num = this.parseValue(value);
36556         
36557         if(isNaN(num)){
36558             this.markInvalid(String.format(this.nanText, value));
36559             return false;
36560         }
36561         
36562         if(num < this.minValue){
36563             this.markInvalid(String.format(this.minText, this.minValue));
36564             return false;
36565         }
36566         
36567         if(num > this.maxValue){
36568             this.markInvalid(String.format(this.maxText, this.maxValue));
36569             return false;
36570         }
36571         
36572         return true;
36573     },
36574
36575     getValue : function()
36576     {
36577         var v = this.hiddenEl().getValue();
36578         
36579         return this.fixPrecision(this.parseValue(v));
36580     },
36581
36582     parseValue : function(value)
36583     {
36584         if(this.thousandsDelimiter) {
36585             value += "";
36586             r = new RegExp(",", "g");
36587             value = value.replace(r, "");
36588         }
36589         
36590         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36591         return isNaN(value) ? '' : value;
36592     },
36593
36594     fixPrecision : function(value)
36595     {
36596         if(this.thousandsDelimiter) {
36597             value += "";
36598             r = new RegExp(",", "g");
36599             value = value.replace(r, "");
36600         }
36601         
36602         var nan = isNaN(value);
36603         
36604         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36605             return nan ? '' : value;
36606         }
36607         return parseFloat(value).toFixed(this.decimalPrecision);
36608     },
36609
36610     setValue : function(v)
36611     {
36612         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36613         
36614         this.value = v;
36615         
36616         if(this.rendered){
36617             
36618             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36619             
36620             this.inputEl().dom.value = (v == '') ? '' :
36621                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36622             
36623             if(!this.allowZero && v === '0') {
36624                 this.hiddenEl().dom.value = '';
36625                 this.inputEl().dom.value = '';
36626             }
36627             
36628             this.validate();
36629         }
36630     },
36631
36632     decimalPrecisionFcn : function(v)
36633     {
36634         return Math.floor(v);
36635     },
36636
36637     beforeBlur : function()
36638     {
36639         var v = this.parseValue(this.getRawValue());
36640         
36641         if(v || v === 0 || v === ''){
36642             this.setValue(v);
36643         }
36644     },
36645     
36646     hiddenEl : function()
36647     {
36648         return this.el.select('input.hidden-number-input',true).first();
36649     }
36650     
36651 });
36652
36653  
36654
36655 /*
36656 * Licence: LGPL
36657 */
36658
36659 /**
36660  * @class Roo.bootstrap.DocumentSlider
36661  * @extends Roo.bootstrap.Component
36662  * Bootstrap DocumentSlider class
36663  * 
36664  * @constructor
36665  * Create a new DocumentViewer
36666  * @param {Object} config The config object
36667  */
36668
36669 Roo.bootstrap.DocumentSlider = function(config){
36670     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36671     
36672     this.files = [];
36673     
36674     this.addEvents({
36675         /**
36676          * @event initial
36677          * Fire after initEvent
36678          * @param {Roo.bootstrap.DocumentSlider} this
36679          */
36680         "initial" : true,
36681         /**
36682          * @event update
36683          * Fire after update
36684          * @param {Roo.bootstrap.DocumentSlider} this
36685          */
36686         "update" : true,
36687         /**
36688          * @event click
36689          * Fire after click
36690          * @param {Roo.bootstrap.DocumentSlider} this
36691          */
36692         "click" : true
36693     });
36694 };
36695
36696 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36697     
36698     files : false,
36699     
36700     indicator : 0,
36701     
36702     getAutoCreate : function()
36703     {
36704         var cfg = {
36705             tag : 'div',
36706             cls : 'roo-document-slider',
36707             cn : [
36708                 {
36709                     tag : 'div',
36710                     cls : 'roo-document-slider-header',
36711                     cn : [
36712                         {
36713                             tag : 'div',
36714                             cls : 'roo-document-slider-header-title'
36715                         }
36716                     ]
36717                 },
36718                 {
36719                     tag : 'div',
36720                     cls : 'roo-document-slider-body',
36721                     cn : [
36722                         {
36723                             tag : 'div',
36724                             cls : 'roo-document-slider-prev',
36725                             cn : [
36726                                 {
36727                                     tag : 'i',
36728                                     cls : 'fa fa-chevron-left'
36729                                 }
36730                             ]
36731                         },
36732                         {
36733                             tag : 'div',
36734                             cls : 'roo-document-slider-thumb',
36735                             cn : [
36736                                 {
36737                                     tag : 'img',
36738                                     cls : 'roo-document-slider-image'
36739                                 }
36740                             ]
36741                         },
36742                         {
36743                             tag : 'div',
36744                             cls : 'roo-document-slider-next',
36745                             cn : [
36746                                 {
36747                                     tag : 'i',
36748                                     cls : 'fa fa-chevron-right'
36749                                 }
36750                             ]
36751                         }
36752                     ]
36753                 }
36754             ]
36755         };
36756         
36757         return cfg;
36758     },
36759     
36760     initEvents : function()
36761     {
36762         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36763         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36764         
36765         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36766         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36767         
36768         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36769         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36770         
36771         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36772         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36773         
36774         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36775         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36776         
36777         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36778         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36779         
36780         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36781         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36782         
36783         this.thumbEl.on('click', this.onClick, this);
36784         
36785         this.prevIndicator.on('click', this.prev, this);
36786         
36787         this.nextIndicator.on('click', this.next, this);
36788         
36789     },
36790     
36791     initial : function()
36792     {
36793         if(this.files.length){
36794             this.indicator = 1;
36795             this.update()
36796         }
36797         
36798         this.fireEvent('initial', this);
36799     },
36800     
36801     update : function()
36802     {
36803         this.imageEl.attr('src', this.files[this.indicator - 1]);
36804         
36805         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36806         
36807         this.prevIndicator.show();
36808         
36809         if(this.indicator == 1){
36810             this.prevIndicator.hide();
36811         }
36812         
36813         this.nextIndicator.show();
36814         
36815         if(this.indicator == this.files.length){
36816             this.nextIndicator.hide();
36817         }
36818         
36819         this.thumbEl.scrollTo('top');
36820         
36821         this.fireEvent('update', this);
36822     },
36823     
36824     onClick : function(e)
36825     {
36826         e.preventDefault();
36827         
36828         this.fireEvent('click', this);
36829     },
36830     
36831     prev : function(e)
36832     {
36833         e.preventDefault();
36834         
36835         this.indicator = Math.max(1, this.indicator - 1);
36836         
36837         this.update();
36838     },
36839     
36840     next : function(e)
36841     {
36842         e.preventDefault();
36843         
36844         this.indicator = Math.min(this.files.length, this.indicator + 1);
36845         
36846         this.update();
36847     }
36848 });
36849 /*
36850  * - LGPL
36851  *
36852  * RadioSet
36853  *
36854  *
36855  */
36856
36857 /**
36858  * @class Roo.bootstrap.RadioSet
36859  * @extends Roo.bootstrap.Input
36860  * Bootstrap RadioSet class
36861  * @cfg {String} indicatorpos (left|right) default left
36862  * @cfg {Boolean} inline (true|false) inline the element (default true)
36863  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36864  * @constructor
36865  * Create a new RadioSet
36866  * @param {Object} config The config object
36867  */
36868
36869 Roo.bootstrap.RadioSet = function(config){
36870     
36871     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36872     
36873     this.radioes = [];
36874     
36875     Roo.bootstrap.RadioSet.register(this);
36876     
36877     this.addEvents({
36878         /**
36879         * @event check
36880         * Fires when the element is checked or unchecked.
36881         * @param {Roo.bootstrap.RadioSet} this This radio
36882         * @param {Roo.bootstrap.Radio} item The checked item
36883         */
36884        check : true,
36885        /**
36886         * @event click
36887         * Fires when the element is click.
36888         * @param {Roo.bootstrap.RadioSet} this This radio set
36889         * @param {Roo.bootstrap.Radio} item The checked item
36890         * @param {Roo.EventObject} e The event object
36891         */
36892        click : true
36893     });
36894     
36895 };
36896
36897 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36898
36899     radioes : false,
36900     
36901     inline : true,
36902     
36903     weight : '',
36904     
36905     indicatorpos : 'left',
36906     
36907     getAutoCreate : function()
36908     {
36909         var label = {
36910             tag : 'label',
36911             cls : 'roo-radio-set-label',
36912             cn : [
36913                 {
36914                     tag : 'span',
36915                     html : this.fieldLabel
36916                 }
36917             ]
36918         };
36919         if (Roo.bootstrap.version == 3) {
36920             
36921             
36922             if(this.indicatorpos == 'left'){
36923                 label.cn.unshift({
36924                     tag : 'i',
36925                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36926                     tooltip : 'This field is required'
36927                 });
36928             } else {
36929                 label.cn.push({
36930                     tag : 'i',
36931                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36932                     tooltip : 'This field is required'
36933                 });
36934             }
36935         }
36936         var items = {
36937             tag : 'div',
36938             cls : 'roo-radio-set-items'
36939         };
36940         
36941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36942         
36943         if (align === 'left' && this.fieldLabel.length) {
36944             
36945             items = {
36946                 cls : "roo-radio-set-right", 
36947                 cn: [
36948                     items
36949                 ]
36950             };
36951             
36952             if(this.labelWidth > 12){
36953                 label.style = "width: " + this.labelWidth + 'px';
36954             }
36955             
36956             if(this.labelWidth < 13 && this.labelmd == 0){
36957                 this.labelmd = this.labelWidth;
36958             }
36959             
36960             if(this.labellg > 0){
36961                 label.cls += ' col-lg-' + this.labellg;
36962                 items.cls += ' col-lg-' + (12 - this.labellg);
36963             }
36964             
36965             if(this.labelmd > 0){
36966                 label.cls += ' col-md-' + this.labelmd;
36967                 items.cls += ' col-md-' + (12 - this.labelmd);
36968             }
36969             
36970             if(this.labelsm > 0){
36971                 label.cls += ' col-sm-' + this.labelsm;
36972                 items.cls += ' col-sm-' + (12 - this.labelsm);
36973             }
36974             
36975             if(this.labelxs > 0){
36976                 label.cls += ' col-xs-' + this.labelxs;
36977                 items.cls += ' col-xs-' + (12 - this.labelxs);
36978             }
36979         }
36980         
36981         var cfg = {
36982             tag : 'div',
36983             cls : 'roo-radio-set',
36984             cn : [
36985                 {
36986                     tag : 'input',
36987                     cls : 'roo-radio-set-input',
36988                     type : 'hidden',
36989                     name : this.name,
36990                     value : this.value ? this.value :  ''
36991                 },
36992                 label,
36993                 items
36994             ]
36995         };
36996         
36997         if(this.weight.length){
36998             cfg.cls += ' roo-radio-' + this.weight;
36999         }
37000         
37001         if(this.inline) {
37002             cfg.cls += ' roo-radio-set-inline';
37003         }
37004         
37005         var settings=this;
37006         ['xs','sm','md','lg'].map(function(size){
37007             if (settings[size]) {
37008                 cfg.cls += ' col-' + size + '-' + settings[size];
37009             }
37010         });
37011         
37012         return cfg;
37013         
37014     },
37015
37016     initEvents : function()
37017     {
37018         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37019         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37020         
37021         if(!this.fieldLabel.length){
37022             this.labelEl.hide();
37023         }
37024         
37025         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37026         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37027         
37028         this.indicator = this.indicatorEl();
37029         
37030         if(this.indicator){
37031             this.indicator.addClass('invisible');
37032         }
37033         
37034         this.originalValue = this.getValue();
37035         
37036     },
37037     
37038     inputEl: function ()
37039     {
37040         return this.el.select('.roo-radio-set-input', true).first();
37041     },
37042     
37043     getChildContainer : function()
37044     {
37045         return this.itemsEl;
37046     },
37047     
37048     register : function(item)
37049     {
37050         this.radioes.push(item);
37051         
37052     },
37053     
37054     validate : function()
37055     {   
37056         if(this.getVisibilityEl().hasClass('hidden')){
37057             return true;
37058         }
37059         
37060         var valid = false;
37061         
37062         Roo.each(this.radioes, function(i){
37063             if(!i.checked){
37064                 return;
37065             }
37066             
37067             valid = true;
37068             return false;
37069         });
37070         
37071         if(this.allowBlank) {
37072             return true;
37073         }
37074         
37075         if(this.disabled || valid){
37076             this.markValid();
37077             return true;
37078         }
37079         
37080         this.markInvalid();
37081         return false;
37082         
37083     },
37084     
37085     markValid : function()
37086     {
37087         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37088             this.indicatorEl().removeClass('visible');
37089             this.indicatorEl().addClass('invisible');
37090         }
37091         
37092         
37093         if (Roo.bootstrap.version == 3) {
37094             this.el.removeClass([this.invalidClass, this.validClass]);
37095             this.el.addClass(this.validClass);
37096         } else {
37097             this.el.removeClass(['is-invalid','is-valid']);
37098             this.el.addClass(['is-valid']);
37099         }
37100         this.fireEvent('valid', this);
37101     },
37102     
37103     markInvalid : function(msg)
37104     {
37105         if(this.allowBlank || this.disabled){
37106             return;
37107         }
37108         
37109         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37110             this.indicatorEl().removeClass('invisible');
37111             this.indicatorEl().addClass('visible');
37112         }
37113         if (Roo.bootstrap.version == 3) {
37114             this.el.removeClass([this.invalidClass, this.validClass]);
37115             this.el.addClass(this.invalidClass);
37116         } else {
37117             this.el.removeClass(['is-invalid','is-valid']);
37118             this.el.addClass(['is-invalid']);
37119         }
37120         
37121         this.fireEvent('invalid', this, msg);
37122         
37123     },
37124     
37125     setValue : function(v, suppressEvent)
37126     {   
37127         if(this.value === v){
37128             return;
37129         }
37130         
37131         this.value = v;
37132         
37133         if(this.rendered){
37134             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37135         }
37136         
37137         Roo.each(this.radioes, function(i){
37138             i.checked = false;
37139             i.el.removeClass('checked');
37140         });
37141         
37142         Roo.each(this.radioes, function(i){
37143             
37144             if(i.value === v || i.value.toString() === v.toString()){
37145                 i.checked = true;
37146                 i.el.addClass('checked');
37147                 
37148                 if(suppressEvent !== true){
37149                     this.fireEvent('check', this, i);
37150                 }
37151                 
37152                 return false;
37153             }
37154             
37155         }, this);
37156         
37157         this.validate();
37158     },
37159     
37160     clearInvalid : function(){
37161         
37162         if(!this.el || this.preventMark){
37163             return;
37164         }
37165         
37166         this.el.removeClass([this.invalidClass]);
37167         
37168         this.fireEvent('valid', this);
37169     }
37170     
37171 });
37172
37173 Roo.apply(Roo.bootstrap.RadioSet, {
37174     
37175     groups: {},
37176     
37177     register : function(set)
37178     {
37179         this.groups[set.name] = set;
37180     },
37181     
37182     get: function(name) 
37183     {
37184         if (typeof(this.groups[name]) == 'undefined') {
37185             return false;
37186         }
37187         
37188         return this.groups[name] ;
37189     }
37190     
37191 });
37192 /*
37193  * Based on:
37194  * Ext JS Library 1.1.1
37195  * Copyright(c) 2006-2007, Ext JS, LLC.
37196  *
37197  * Originally Released Under LGPL - original licence link has changed is not relivant.
37198  *
37199  * Fork - LGPL
37200  * <script type="text/javascript">
37201  */
37202
37203
37204 /**
37205  * @class Roo.bootstrap.SplitBar
37206  * @extends Roo.util.Observable
37207  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37208  * <br><br>
37209  * Usage:
37210  * <pre><code>
37211 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37212                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37213 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37214 split.minSize = 100;
37215 split.maxSize = 600;
37216 split.animate = true;
37217 split.on('moved', splitterMoved);
37218 </code></pre>
37219  * @constructor
37220  * Create a new SplitBar
37221  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37222  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37223  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37224  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37225                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37226                         position of the SplitBar).
37227  */
37228 Roo.bootstrap.SplitBar = function(cfg){
37229     
37230     /** @private */
37231     
37232     //{
37233     //  dragElement : elm
37234     //  resizingElement: el,
37235         // optional..
37236     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37237     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37238         // existingProxy ???
37239     //}
37240     
37241     this.el = Roo.get(cfg.dragElement, true);
37242     this.el.dom.unselectable = "on";
37243     /** @private */
37244     this.resizingEl = Roo.get(cfg.resizingElement, true);
37245
37246     /**
37247      * @private
37248      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37249      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37250      * @type Number
37251      */
37252     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37253     
37254     /**
37255      * The minimum size of the resizing element. (Defaults to 0)
37256      * @type Number
37257      */
37258     this.minSize = 0;
37259     
37260     /**
37261      * The maximum size of the resizing element. (Defaults to 2000)
37262      * @type Number
37263      */
37264     this.maxSize = 2000;
37265     
37266     /**
37267      * Whether to animate the transition to the new size
37268      * @type Boolean
37269      */
37270     this.animate = false;
37271     
37272     /**
37273      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37274      * @type Boolean
37275      */
37276     this.useShim = false;
37277     
37278     /** @private */
37279     this.shim = null;
37280     
37281     if(!cfg.existingProxy){
37282         /** @private */
37283         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37284     }else{
37285         this.proxy = Roo.get(cfg.existingProxy).dom;
37286     }
37287     /** @private */
37288     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37289     
37290     /** @private */
37291     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37292     
37293     /** @private */
37294     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37295     
37296     /** @private */
37297     this.dragSpecs = {};
37298     
37299     /**
37300      * @private The adapter to use to positon and resize elements
37301      */
37302     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37303     this.adapter.init(this);
37304     
37305     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37306         /** @private */
37307         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37308         this.el.addClass("roo-splitbar-h");
37309     }else{
37310         /** @private */
37311         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37312         this.el.addClass("roo-splitbar-v");
37313     }
37314     
37315     this.addEvents({
37316         /**
37317          * @event resize
37318          * Fires when the splitter is moved (alias for {@link #event-moved})
37319          * @param {Roo.bootstrap.SplitBar} this
37320          * @param {Number} newSize the new width or height
37321          */
37322         "resize" : true,
37323         /**
37324          * @event moved
37325          * Fires when the splitter is moved
37326          * @param {Roo.bootstrap.SplitBar} this
37327          * @param {Number} newSize the new width or height
37328          */
37329         "moved" : true,
37330         /**
37331          * @event beforeresize
37332          * Fires before the splitter is dragged
37333          * @param {Roo.bootstrap.SplitBar} this
37334          */
37335         "beforeresize" : true,
37336
37337         "beforeapply" : true
37338     });
37339
37340     Roo.util.Observable.call(this);
37341 };
37342
37343 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37344     onStartProxyDrag : function(x, y){
37345         this.fireEvent("beforeresize", this);
37346         if(!this.overlay){
37347             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37348             o.unselectable();
37349             o.enableDisplayMode("block");
37350             // all splitbars share the same overlay
37351             Roo.bootstrap.SplitBar.prototype.overlay = o;
37352         }
37353         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37354         this.overlay.show();
37355         Roo.get(this.proxy).setDisplayed("block");
37356         var size = this.adapter.getElementSize(this);
37357         this.activeMinSize = this.getMinimumSize();;
37358         this.activeMaxSize = this.getMaximumSize();;
37359         var c1 = size - this.activeMinSize;
37360         var c2 = Math.max(this.activeMaxSize - size, 0);
37361         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37362             this.dd.resetConstraints();
37363             this.dd.setXConstraint(
37364                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37365                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37366             );
37367             this.dd.setYConstraint(0, 0);
37368         }else{
37369             this.dd.resetConstraints();
37370             this.dd.setXConstraint(0, 0);
37371             this.dd.setYConstraint(
37372                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37373                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37374             );
37375          }
37376         this.dragSpecs.startSize = size;
37377         this.dragSpecs.startPoint = [x, y];
37378         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37379     },
37380     
37381     /** 
37382      * @private Called after the drag operation by the DDProxy
37383      */
37384     onEndProxyDrag : function(e){
37385         Roo.get(this.proxy).setDisplayed(false);
37386         var endPoint = Roo.lib.Event.getXY(e);
37387         if(this.overlay){
37388             this.overlay.hide();
37389         }
37390         var newSize;
37391         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37392             newSize = this.dragSpecs.startSize + 
37393                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37394                     endPoint[0] - this.dragSpecs.startPoint[0] :
37395                     this.dragSpecs.startPoint[0] - endPoint[0]
37396                 );
37397         }else{
37398             newSize = this.dragSpecs.startSize + 
37399                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37400                     endPoint[1] - this.dragSpecs.startPoint[1] :
37401                     this.dragSpecs.startPoint[1] - endPoint[1]
37402                 );
37403         }
37404         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37405         if(newSize != this.dragSpecs.startSize){
37406             if(this.fireEvent('beforeapply', this, newSize) !== false){
37407                 this.adapter.setElementSize(this, newSize);
37408                 this.fireEvent("moved", this, newSize);
37409                 this.fireEvent("resize", this, newSize);
37410             }
37411         }
37412     },
37413     
37414     /**
37415      * Get the adapter this SplitBar uses
37416      * @return The adapter object
37417      */
37418     getAdapter : function(){
37419         return this.adapter;
37420     },
37421     
37422     /**
37423      * Set the adapter this SplitBar uses
37424      * @param {Object} adapter A SplitBar adapter object
37425      */
37426     setAdapter : function(adapter){
37427         this.adapter = adapter;
37428         this.adapter.init(this);
37429     },
37430     
37431     /**
37432      * Gets the minimum size for the resizing element
37433      * @return {Number} The minimum size
37434      */
37435     getMinimumSize : function(){
37436         return this.minSize;
37437     },
37438     
37439     /**
37440      * Sets the minimum size for the resizing element
37441      * @param {Number} minSize The minimum size
37442      */
37443     setMinimumSize : function(minSize){
37444         this.minSize = minSize;
37445     },
37446     
37447     /**
37448      * Gets the maximum size for the resizing element
37449      * @return {Number} The maximum size
37450      */
37451     getMaximumSize : function(){
37452         return this.maxSize;
37453     },
37454     
37455     /**
37456      * Sets the maximum size for the resizing element
37457      * @param {Number} maxSize The maximum size
37458      */
37459     setMaximumSize : function(maxSize){
37460         this.maxSize = maxSize;
37461     },
37462     
37463     /**
37464      * Sets the initialize size for the resizing element
37465      * @param {Number} size The initial size
37466      */
37467     setCurrentSize : function(size){
37468         var oldAnimate = this.animate;
37469         this.animate = false;
37470         this.adapter.setElementSize(this, size);
37471         this.animate = oldAnimate;
37472     },
37473     
37474     /**
37475      * Destroy this splitbar. 
37476      * @param {Boolean} removeEl True to remove the element
37477      */
37478     destroy : function(removeEl){
37479         if(this.shim){
37480             this.shim.remove();
37481         }
37482         this.dd.unreg();
37483         this.proxy.parentNode.removeChild(this.proxy);
37484         if(removeEl){
37485             this.el.remove();
37486         }
37487     }
37488 });
37489
37490 /**
37491  * @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.
37492  */
37493 Roo.bootstrap.SplitBar.createProxy = function(dir){
37494     var proxy = new Roo.Element(document.createElement("div"));
37495     proxy.unselectable();
37496     var cls = 'roo-splitbar-proxy';
37497     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37498     document.body.appendChild(proxy.dom);
37499     return proxy.dom;
37500 };
37501
37502 /** 
37503  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37504  * Default Adapter. It assumes the splitter and resizing element are not positioned
37505  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37506  */
37507 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37508 };
37509
37510 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37511     // do nothing for now
37512     init : function(s){
37513     
37514     },
37515     /**
37516      * Called before drag operations to get the current size of the resizing element. 
37517      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37518      */
37519      getElementSize : function(s){
37520         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37521             return s.resizingEl.getWidth();
37522         }else{
37523             return s.resizingEl.getHeight();
37524         }
37525     },
37526     
37527     /**
37528      * Called after drag operations to set the size of the resizing element.
37529      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37530      * @param {Number} newSize The new size to set
37531      * @param {Function} onComplete A function to be invoked when resizing is complete
37532      */
37533     setElementSize : function(s, newSize, onComplete){
37534         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37535             if(!s.animate){
37536                 s.resizingEl.setWidth(newSize);
37537                 if(onComplete){
37538                     onComplete(s, newSize);
37539                 }
37540             }else{
37541                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37542             }
37543         }else{
37544             
37545             if(!s.animate){
37546                 s.resizingEl.setHeight(newSize);
37547                 if(onComplete){
37548                     onComplete(s, newSize);
37549                 }
37550             }else{
37551                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37552             }
37553         }
37554     }
37555 };
37556
37557 /** 
37558  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37559  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37560  * Adapter that  moves the splitter element to align with the resized sizing element. 
37561  * Used with an absolute positioned SplitBar.
37562  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37563  * document.body, make sure you assign an id to the body element.
37564  */
37565 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37566     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37567     this.container = Roo.get(container);
37568 };
37569
37570 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37571     init : function(s){
37572         this.basic.init(s);
37573     },
37574     
37575     getElementSize : function(s){
37576         return this.basic.getElementSize(s);
37577     },
37578     
37579     setElementSize : function(s, newSize, onComplete){
37580         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37581     },
37582     
37583     moveSplitter : function(s){
37584         var yes = Roo.bootstrap.SplitBar;
37585         switch(s.placement){
37586             case yes.LEFT:
37587                 s.el.setX(s.resizingEl.getRight());
37588                 break;
37589             case yes.RIGHT:
37590                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37591                 break;
37592             case yes.TOP:
37593                 s.el.setY(s.resizingEl.getBottom());
37594                 break;
37595             case yes.BOTTOM:
37596                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37597                 break;
37598         }
37599     }
37600 };
37601
37602 /**
37603  * Orientation constant - Create a vertical SplitBar
37604  * @static
37605  * @type Number
37606  */
37607 Roo.bootstrap.SplitBar.VERTICAL = 1;
37608
37609 /**
37610  * Orientation constant - Create a horizontal SplitBar
37611  * @static
37612  * @type Number
37613  */
37614 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37615
37616 /**
37617  * Placement constant - The resizing element is to the left of the splitter element
37618  * @static
37619  * @type Number
37620  */
37621 Roo.bootstrap.SplitBar.LEFT = 1;
37622
37623 /**
37624  * Placement constant - The resizing element is to the right of the splitter element
37625  * @static
37626  * @type Number
37627  */
37628 Roo.bootstrap.SplitBar.RIGHT = 2;
37629
37630 /**
37631  * Placement constant - The resizing element is positioned above the splitter element
37632  * @static
37633  * @type Number
37634  */
37635 Roo.bootstrap.SplitBar.TOP = 3;
37636
37637 /**
37638  * Placement constant - The resizing element is positioned under splitter element
37639  * @static
37640  * @type Number
37641  */
37642 Roo.bootstrap.SplitBar.BOTTOM = 4;
37643 Roo.namespace("Roo.bootstrap.layout");/*
37644  * Based on:
37645  * Ext JS Library 1.1.1
37646  * Copyright(c) 2006-2007, Ext JS, LLC.
37647  *
37648  * Originally Released Under LGPL - original licence link has changed is not relivant.
37649  *
37650  * Fork - LGPL
37651  * <script type="text/javascript">
37652  */
37653
37654 /**
37655  * @class Roo.bootstrap.layout.Manager
37656  * @extends Roo.bootstrap.Component
37657  * Base class for layout managers.
37658  */
37659 Roo.bootstrap.layout.Manager = function(config)
37660 {
37661     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37662
37663
37664
37665
37666
37667     /** false to disable window resize monitoring @type Boolean */
37668     this.monitorWindowResize = true;
37669     this.regions = {};
37670     this.addEvents({
37671         /**
37672          * @event layout
37673          * Fires when a layout is performed.
37674          * @param {Roo.LayoutManager} this
37675          */
37676         "layout" : true,
37677         /**
37678          * @event regionresized
37679          * Fires when the user resizes a region.
37680          * @param {Roo.LayoutRegion} region The resized region
37681          * @param {Number} newSize The new size (width for east/west, height for north/south)
37682          */
37683         "regionresized" : true,
37684         /**
37685          * @event regioncollapsed
37686          * Fires when a region is collapsed.
37687          * @param {Roo.LayoutRegion} region The collapsed region
37688          */
37689         "regioncollapsed" : true,
37690         /**
37691          * @event regionexpanded
37692          * Fires when a region is expanded.
37693          * @param {Roo.LayoutRegion} region The expanded region
37694          */
37695         "regionexpanded" : true
37696     });
37697     this.updating = false;
37698
37699     if (config.el) {
37700         this.el = Roo.get(config.el);
37701         this.initEvents();
37702     }
37703
37704 };
37705
37706 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37707
37708
37709     regions : null,
37710
37711     monitorWindowResize : true,
37712
37713
37714     updating : false,
37715
37716
37717     onRender : function(ct, position)
37718     {
37719         if(!this.el){
37720             this.el = Roo.get(ct);
37721             this.initEvents();
37722         }
37723         //this.fireEvent('render',this);
37724     },
37725
37726
37727     initEvents: function()
37728     {
37729
37730
37731         // ie scrollbar fix
37732         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37733             document.body.scroll = "no";
37734         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37735             this.el.position('relative');
37736         }
37737         this.id = this.el.id;
37738         this.el.addClass("roo-layout-container");
37739         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37740         if(this.el.dom != document.body ) {
37741             this.el.on('resize', this.layout,this);
37742             this.el.on('show', this.layout,this);
37743         }
37744
37745     },
37746
37747     /**
37748      * Returns true if this layout is currently being updated
37749      * @return {Boolean}
37750      */
37751     isUpdating : function(){
37752         return this.updating;
37753     },
37754
37755     /**
37756      * Suspend the LayoutManager from doing auto-layouts while
37757      * making multiple add or remove calls
37758      */
37759     beginUpdate : function(){
37760         this.updating = true;
37761     },
37762
37763     /**
37764      * Restore auto-layouts and optionally disable the manager from performing a layout
37765      * @param {Boolean} noLayout true to disable a layout update
37766      */
37767     endUpdate : function(noLayout){
37768         this.updating = false;
37769         if(!noLayout){
37770             this.layout();
37771         }
37772     },
37773
37774     layout: function(){
37775         // abstract...
37776     },
37777
37778     onRegionResized : function(region, newSize){
37779         this.fireEvent("regionresized", region, newSize);
37780         this.layout();
37781     },
37782
37783     onRegionCollapsed : function(region){
37784         this.fireEvent("regioncollapsed", region);
37785     },
37786
37787     onRegionExpanded : function(region){
37788         this.fireEvent("regionexpanded", region);
37789     },
37790
37791     /**
37792      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37793      * performs box-model adjustments.
37794      * @return {Object} The size as an object {width: (the width), height: (the height)}
37795      */
37796     getViewSize : function()
37797     {
37798         var size;
37799         if(this.el.dom != document.body){
37800             size = this.el.getSize();
37801         }else{
37802             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37803         }
37804         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37805         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37806         return size;
37807     },
37808
37809     /**
37810      * Returns the Element this layout is bound to.
37811      * @return {Roo.Element}
37812      */
37813     getEl : function(){
37814         return this.el;
37815     },
37816
37817     /**
37818      * Returns the specified region.
37819      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37820      * @return {Roo.LayoutRegion}
37821      */
37822     getRegion : function(target){
37823         return this.regions[target.toLowerCase()];
37824     },
37825
37826     onWindowResize : function(){
37827         if(this.monitorWindowResize){
37828             this.layout();
37829         }
37830     }
37831 });
37832 /*
37833  * Based on:
37834  * Ext JS Library 1.1.1
37835  * Copyright(c) 2006-2007, Ext JS, LLC.
37836  *
37837  * Originally Released Under LGPL - original licence link has changed is not relivant.
37838  *
37839  * Fork - LGPL
37840  * <script type="text/javascript">
37841  */
37842 /**
37843  * @class Roo.bootstrap.layout.Border
37844  * @extends Roo.bootstrap.layout.Manager
37845  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37846  * please see: examples/bootstrap/nested.html<br><br>
37847  
37848 <b>The container the layout is rendered into can be either the body element or any other element.
37849 If it is not the body element, the container needs to either be an absolute positioned element,
37850 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37851 the container size if it is not the body element.</b>
37852
37853 * @constructor
37854 * Create a new Border
37855 * @param {Object} config Configuration options
37856  */
37857 Roo.bootstrap.layout.Border = function(config){
37858     config = config || {};
37859     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37860     
37861     
37862     
37863     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37864         if(config[region]){
37865             config[region].region = region;
37866             this.addRegion(config[region]);
37867         }
37868     },this);
37869     
37870 };
37871
37872 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37873
37874 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37875     
37876     parent : false, // this might point to a 'nest' or a ???
37877     
37878     /**
37879      * Creates and adds a new region if it doesn't already exist.
37880      * @param {String} target The target region key (north, south, east, west or center).
37881      * @param {Object} config The regions config object
37882      * @return {BorderLayoutRegion} The new region
37883      */
37884     addRegion : function(config)
37885     {
37886         if(!this.regions[config.region]){
37887             var r = this.factory(config);
37888             this.bindRegion(r);
37889         }
37890         return this.regions[config.region];
37891     },
37892
37893     // private (kinda)
37894     bindRegion : function(r){
37895         this.regions[r.config.region] = r;
37896         
37897         r.on("visibilitychange",    this.layout, this);
37898         r.on("paneladded",          this.layout, this);
37899         r.on("panelremoved",        this.layout, this);
37900         r.on("invalidated",         this.layout, this);
37901         r.on("resized",             this.onRegionResized, this);
37902         r.on("collapsed",           this.onRegionCollapsed, this);
37903         r.on("expanded",            this.onRegionExpanded, this);
37904     },
37905
37906     /**
37907      * Performs a layout update.
37908      */
37909     layout : function()
37910     {
37911         if(this.updating) {
37912             return;
37913         }
37914         
37915         // render all the rebions if they have not been done alreayd?
37916         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37917             if(this.regions[region] && !this.regions[region].bodyEl){
37918                 this.regions[region].onRender(this.el)
37919             }
37920         },this);
37921         
37922         var size = this.getViewSize();
37923         var w = size.width;
37924         var h = size.height;
37925         var centerW = w;
37926         var centerH = h;
37927         var centerY = 0;
37928         var centerX = 0;
37929         //var x = 0, y = 0;
37930
37931         var rs = this.regions;
37932         var north = rs["north"];
37933         var south = rs["south"]; 
37934         var west = rs["west"];
37935         var east = rs["east"];
37936         var center = rs["center"];
37937         //if(this.hideOnLayout){ // not supported anymore
37938             //c.el.setStyle("display", "none");
37939         //}
37940         if(north && north.isVisible()){
37941             var b = north.getBox();
37942             var m = north.getMargins();
37943             b.width = w - (m.left+m.right);
37944             b.x = m.left;
37945             b.y = m.top;
37946             centerY = b.height + b.y + m.bottom;
37947             centerH -= centerY;
37948             north.updateBox(this.safeBox(b));
37949         }
37950         if(south && south.isVisible()){
37951             var b = south.getBox();
37952             var m = south.getMargins();
37953             b.width = w - (m.left+m.right);
37954             b.x = m.left;
37955             var totalHeight = (b.height + m.top + m.bottom);
37956             b.y = h - totalHeight + m.top;
37957             centerH -= totalHeight;
37958             south.updateBox(this.safeBox(b));
37959         }
37960         if(west && west.isVisible()){
37961             var b = west.getBox();
37962             var m = west.getMargins();
37963             b.height = centerH - (m.top+m.bottom);
37964             b.x = m.left;
37965             b.y = centerY + m.top;
37966             var totalWidth = (b.width + m.left + m.right);
37967             centerX += totalWidth;
37968             centerW -= totalWidth;
37969             west.updateBox(this.safeBox(b));
37970         }
37971         if(east && east.isVisible()){
37972             var b = east.getBox();
37973             var m = east.getMargins();
37974             b.height = centerH - (m.top+m.bottom);
37975             var totalWidth = (b.width + m.left + m.right);
37976             b.x = w - totalWidth + m.left;
37977             b.y = centerY + m.top;
37978             centerW -= totalWidth;
37979             east.updateBox(this.safeBox(b));
37980         }
37981         if(center){
37982             var m = center.getMargins();
37983             var centerBox = {
37984                 x: centerX + m.left,
37985                 y: centerY + m.top,
37986                 width: centerW - (m.left+m.right),
37987                 height: centerH - (m.top+m.bottom)
37988             };
37989             //if(this.hideOnLayout){
37990                 //center.el.setStyle("display", "block");
37991             //}
37992             center.updateBox(this.safeBox(centerBox));
37993         }
37994         this.el.repaint();
37995         this.fireEvent("layout", this);
37996     },
37997
37998     // private
37999     safeBox : function(box){
38000         box.width = Math.max(0, box.width);
38001         box.height = Math.max(0, box.height);
38002         return box;
38003     },
38004
38005     /**
38006      * Adds a ContentPanel (or subclass) to this layout.
38007      * @param {String} target The target region key (north, south, east, west or center).
38008      * @param {Roo.ContentPanel} panel The panel to add
38009      * @return {Roo.ContentPanel} The added panel
38010      */
38011     add : function(target, panel){
38012          
38013         target = target.toLowerCase();
38014         return this.regions[target].add(panel);
38015     },
38016
38017     /**
38018      * Remove a ContentPanel (or subclass) to this layout.
38019      * @param {String} target The target region key (north, south, east, west or center).
38020      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38021      * @return {Roo.ContentPanel} The removed panel
38022      */
38023     remove : function(target, panel){
38024         target = target.toLowerCase();
38025         return this.regions[target].remove(panel);
38026     },
38027
38028     /**
38029      * Searches all regions for a panel with the specified id
38030      * @param {String} panelId
38031      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38032      */
38033     findPanel : function(panelId){
38034         var rs = this.regions;
38035         for(var target in rs){
38036             if(typeof rs[target] != "function"){
38037                 var p = rs[target].getPanel(panelId);
38038                 if(p){
38039                     return p;
38040                 }
38041             }
38042         }
38043         return null;
38044     },
38045
38046     /**
38047      * Searches all regions for a panel with the specified id and activates (shows) it.
38048      * @param {String/ContentPanel} panelId The panels id or the panel itself
38049      * @return {Roo.ContentPanel} The shown panel or null
38050      */
38051     showPanel : function(panelId) {
38052       var rs = this.regions;
38053       for(var target in rs){
38054          var r = rs[target];
38055          if(typeof r != "function"){
38056             if(r.hasPanel(panelId)){
38057                return r.showPanel(panelId);
38058             }
38059          }
38060       }
38061       return null;
38062    },
38063
38064    /**
38065      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38066      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38067      */
38068    /*
38069     restoreState : function(provider){
38070         if(!provider){
38071             provider = Roo.state.Manager;
38072         }
38073         var sm = new Roo.LayoutStateManager();
38074         sm.init(this, provider);
38075     },
38076 */
38077  
38078  
38079     /**
38080      * Adds a xtype elements to the layout.
38081      * <pre><code>
38082
38083 layout.addxtype({
38084        xtype : 'ContentPanel',
38085        region: 'west',
38086        items: [ .... ]
38087    }
38088 );
38089
38090 layout.addxtype({
38091         xtype : 'NestedLayoutPanel',
38092         region: 'west',
38093         layout: {
38094            center: { },
38095            west: { }   
38096         },
38097         items : [ ... list of content panels or nested layout panels.. ]
38098    }
38099 );
38100 </code></pre>
38101      * @param {Object} cfg Xtype definition of item to add.
38102      */
38103     addxtype : function(cfg)
38104     {
38105         // basically accepts a pannel...
38106         // can accept a layout region..!?!?
38107         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38108         
38109         
38110         // theory?  children can only be panels??
38111         
38112         //if (!cfg.xtype.match(/Panel$/)) {
38113         //    return false;
38114         //}
38115         var ret = false;
38116         
38117         if (typeof(cfg.region) == 'undefined') {
38118             Roo.log("Failed to add Panel, region was not set");
38119             Roo.log(cfg);
38120             return false;
38121         }
38122         var region = cfg.region;
38123         delete cfg.region;
38124         
38125           
38126         var xitems = [];
38127         if (cfg.items) {
38128             xitems = cfg.items;
38129             delete cfg.items;
38130         }
38131         var nb = false;
38132         
38133         if ( region == 'center') {
38134             Roo.log("Center: " + cfg.title);
38135         }
38136         
38137         
38138         switch(cfg.xtype) 
38139         {
38140             case 'Content':  // ContentPanel (el, cfg)
38141             case 'Scroll':  // ContentPanel (el, cfg)
38142             case 'View': 
38143                 cfg.autoCreate = cfg.autoCreate || true;
38144                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38145                 //} else {
38146                 //    var el = this.el.createChild();
38147                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38148                 //}
38149                 
38150                 this.add(region, ret);
38151                 break;
38152             
38153             /*
38154             case 'TreePanel': // our new panel!
38155                 cfg.el = this.el.createChild();
38156                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38157                 this.add(region, ret);
38158                 break;
38159             */
38160             
38161             case 'Nest': 
38162                 // create a new Layout (which is  a Border Layout...
38163                 
38164                 var clayout = cfg.layout;
38165                 clayout.el  = this.el.createChild();
38166                 clayout.items   = clayout.items  || [];
38167                 
38168                 delete cfg.layout;
38169                 
38170                 // replace this exitems with the clayout ones..
38171                 xitems = clayout.items;
38172                  
38173                 // force background off if it's in center...
38174                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38175                     cfg.background = false;
38176                 }
38177                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38178                 
38179                 
38180                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38181                 //console.log('adding nested layout panel '  + cfg.toSource());
38182                 this.add(region, ret);
38183                 nb = {}; /// find first...
38184                 break;
38185             
38186             case 'Grid':
38187                 
38188                 // needs grid and region
38189                 
38190                 //var el = this.getRegion(region).el.createChild();
38191                 /*
38192                  *var el = this.el.createChild();
38193                 // create the grid first...
38194                 cfg.grid.container = el;
38195                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38196                 */
38197                 
38198                 if (region == 'center' && this.active ) {
38199                     cfg.background = false;
38200                 }
38201                 
38202                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38203                 
38204                 this.add(region, ret);
38205                 /*
38206                 if (cfg.background) {
38207                     // render grid on panel activation (if panel background)
38208                     ret.on('activate', function(gp) {
38209                         if (!gp.grid.rendered) {
38210                     //        gp.grid.render(el);
38211                         }
38212                     });
38213                 } else {
38214                   //  cfg.grid.render(el);
38215                 }
38216                 */
38217                 break;
38218            
38219            
38220             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38221                 // it was the old xcomponent building that caused this before.
38222                 // espeically if border is the top element in the tree.
38223                 ret = this;
38224                 break; 
38225                 
38226                     
38227                 
38228                 
38229                 
38230             default:
38231                 /*
38232                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38233                     
38234                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38235                     this.add(region, ret);
38236                 } else {
38237                 */
38238                     Roo.log(cfg);
38239                     throw "Can not add '" + cfg.xtype + "' to Border";
38240                     return null;
38241              
38242                                 
38243              
38244         }
38245         this.beginUpdate();
38246         // add children..
38247         var region = '';
38248         var abn = {};
38249         Roo.each(xitems, function(i)  {
38250             region = nb && i.region ? i.region : false;
38251             
38252             var add = ret.addxtype(i);
38253            
38254             if (region) {
38255                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38256                 if (!i.background) {
38257                     abn[region] = nb[region] ;
38258                 }
38259             }
38260             
38261         });
38262         this.endUpdate();
38263
38264         // make the last non-background panel active..
38265         //if (nb) { Roo.log(abn); }
38266         if (nb) {
38267             
38268             for(var r in abn) {
38269                 region = this.getRegion(r);
38270                 if (region) {
38271                     // tried using nb[r], but it does not work..
38272                      
38273                     region.showPanel(abn[r]);
38274                    
38275                 }
38276             }
38277         }
38278         return ret;
38279         
38280     },
38281     
38282     
38283 // private
38284     factory : function(cfg)
38285     {
38286         
38287         var validRegions = Roo.bootstrap.layout.Border.regions;
38288
38289         var target = cfg.region;
38290         cfg.mgr = this;
38291         
38292         var r = Roo.bootstrap.layout;
38293         Roo.log(target);
38294         switch(target){
38295             case "north":
38296                 return new r.North(cfg);
38297             case "south":
38298                 return new r.South(cfg);
38299             case "east":
38300                 return new r.East(cfg);
38301             case "west":
38302                 return new r.West(cfg);
38303             case "center":
38304                 return new r.Center(cfg);
38305         }
38306         throw 'Layout region "'+target+'" not supported.';
38307     }
38308     
38309     
38310 });
38311  /*
38312  * Based on:
38313  * Ext JS Library 1.1.1
38314  * Copyright(c) 2006-2007, Ext JS, LLC.
38315  *
38316  * Originally Released Under LGPL - original licence link has changed is not relivant.
38317  *
38318  * Fork - LGPL
38319  * <script type="text/javascript">
38320  */
38321  
38322 /**
38323  * @class Roo.bootstrap.layout.Basic
38324  * @extends Roo.util.Observable
38325  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38326  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38327  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38328  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38329  * @cfg {string}   region  the region that it inhabits..
38330  * @cfg {bool}   skipConfig skip config?
38331  * 
38332
38333  */
38334 Roo.bootstrap.layout.Basic = function(config){
38335     
38336     this.mgr = config.mgr;
38337     
38338     this.position = config.region;
38339     
38340     var skipConfig = config.skipConfig;
38341     
38342     this.events = {
38343         /**
38344          * @scope Roo.BasicLayoutRegion
38345          */
38346         
38347         /**
38348          * @event beforeremove
38349          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38350          * @param {Roo.LayoutRegion} this
38351          * @param {Roo.ContentPanel} panel The panel
38352          * @param {Object} e The cancel event object
38353          */
38354         "beforeremove" : true,
38355         /**
38356          * @event invalidated
38357          * Fires when the layout for this region is changed.
38358          * @param {Roo.LayoutRegion} this
38359          */
38360         "invalidated" : true,
38361         /**
38362          * @event visibilitychange
38363          * Fires when this region is shown or hidden 
38364          * @param {Roo.LayoutRegion} this
38365          * @param {Boolean} visibility true or false
38366          */
38367         "visibilitychange" : true,
38368         /**
38369          * @event paneladded
38370          * Fires when a panel is added. 
38371          * @param {Roo.LayoutRegion} this
38372          * @param {Roo.ContentPanel} panel The panel
38373          */
38374         "paneladded" : true,
38375         /**
38376          * @event panelremoved
38377          * Fires when a panel is removed. 
38378          * @param {Roo.LayoutRegion} this
38379          * @param {Roo.ContentPanel} panel The panel
38380          */
38381         "panelremoved" : true,
38382         /**
38383          * @event beforecollapse
38384          * Fires when this region before collapse.
38385          * @param {Roo.LayoutRegion} this
38386          */
38387         "beforecollapse" : true,
38388         /**
38389          * @event collapsed
38390          * Fires when this region is collapsed.
38391          * @param {Roo.LayoutRegion} this
38392          */
38393         "collapsed" : true,
38394         /**
38395          * @event expanded
38396          * Fires when this region is expanded.
38397          * @param {Roo.LayoutRegion} this
38398          */
38399         "expanded" : true,
38400         /**
38401          * @event slideshow
38402          * Fires when this region is slid into view.
38403          * @param {Roo.LayoutRegion} this
38404          */
38405         "slideshow" : true,
38406         /**
38407          * @event slidehide
38408          * Fires when this region slides out of view. 
38409          * @param {Roo.LayoutRegion} this
38410          */
38411         "slidehide" : true,
38412         /**
38413          * @event panelactivated
38414          * Fires when a panel is activated. 
38415          * @param {Roo.LayoutRegion} this
38416          * @param {Roo.ContentPanel} panel The activated panel
38417          */
38418         "panelactivated" : true,
38419         /**
38420          * @event resized
38421          * Fires when the user resizes this region. 
38422          * @param {Roo.LayoutRegion} this
38423          * @param {Number} newSize The new size (width for east/west, height for north/south)
38424          */
38425         "resized" : true
38426     };
38427     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38428     this.panels = new Roo.util.MixedCollection();
38429     this.panels.getKey = this.getPanelId.createDelegate(this);
38430     this.box = null;
38431     this.activePanel = null;
38432     // ensure listeners are added...
38433     
38434     if (config.listeners || config.events) {
38435         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38436             listeners : config.listeners || {},
38437             events : config.events || {}
38438         });
38439     }
38440     
38441     if(skipConfig !== true){
38442         this.applyConfig(config);
38443     }
38444 };
38445
38446 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38447 {
38448     getPanelId : function(p){
38449         return p.getId();
38450     },
38451     
38452     applyConfig : function(config){
38453         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38454         this.config = config;
38455         
38456     },
38457     
38458     /**
38459      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38460      * the width, for horizontal (north, south) the height.
38461      * @param {Number} newSize The new width or height
38462      */
38463     resizeTo : function(newSize){
38464         var el = this.el ? this.el :
38465                  (this.activePanel ? this.activePanel.getEl() : null);
38466         if(el){
38467             switch(this.position){
38468                 case "east":
38469                 case "west":
38470                     el.setWidth(newSize);
38471                     this.fireEvent("resized", this, newSize);
38472                 break;
38473                 case "north":
38474                 case "south":
38475                     el.setHeight(newSize);
38476                     this.fireEvent("resized", this, newSize);
38477                 break;                
38478             }
38479         }
38480     },
38481     
38482     getBox : function(){
38483         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38484     },
38485     
38486     getMargins : function(){
38487         return this.margins;
38488     },
38489     
38490     updateBox : function(box){
38491         this.box = box;
38492         var el = this.activePanel.getEl();
38493         el.dom.style.left = box.x + "px";
38494         el.dom.style.top = box.y + "px";
38495         this.activePanel.setSize(box.width, box.height);
38496     },
38497     
38498     /**
38499      * Returns the container element for this region.
38500      * @return {Roo.Element}
38501      */
38502     getEl : function(){
38503         return this.activePanel;
38504     },
38505     
38506     /**
38507      * Returns true if this region is currently visible.
38508      * @return {Boolean}
38509      */
38510     isVisible : function(){
38511         return this.activePanel ? true : false;
38512     },
38513     
38514     setActivePanel : function(panel){
38515         panel = this.getPanel(panel);
38516         if(this.activePanel && this.activePanel != panel){
38517             this.activePanel.setActiveState(false);
38518             this.activePanel.getEl().setLeftTop(-10000,-10000);
38519         }
38520         this.activePanel = panel;
38521         panel.setActiveState(true);
38522         if(this.box){
38523             panel.setSize(this.box.width, this.box.height);
38524         }
38525         this.fireEvent("panelactivated", this, panel);
38526         this.fireEvent("invalidated");
38527     },
38528     
38529     /**
38530      * Show the specified panel.
38531      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38532      * @return {Roo.ContentPanel} The shown panel or null
38533      */
38534     showPanel : function(panel){
38535         panel = this.getPanel(panel);
38536         if(panel){
38537             this.setActivePanel(panel);
38538         }
38539         return panel;
38540     },
38541     
38542     /**
38543      * Get the active panel for this region.
38544      * @return {Roo.ContentPanel} The active panel or null
38545      */
38546     getActivePanel : function(){
38547         return this.activePanel;
38548     },
38549     
38550     /**
38551      * Add the passed ContentPanel(s)
38552      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38553      * @return {Roo.ContentPanel} The panel added (if only one was added)
38554      */
38555     add : function(panel){
38556         if(arguments.length > 1){
38557             for(var i = 0, len = arguments.length; i < len; i++) {
38558                 this.add(arguments[i]);
38559             }
38560             return null;
38561         }
38562         if(this.hasPanel(panel)){
38563             this.showPanel(panel);
38564             return panel;
38565         }
38566         var el = panel.getEl();
38567         if(el.dom.parentNode != this.mgr.el.dom){
38568             this.mgr.el.dom.appendChild(el.dom);
38569         }
38570         if(panel.setRegion){
38571             panel.setRegion(this);
38572         }
38573         this.panels.add(panel);
38574         el.setStyle("position", "absolute");
38575         if(!panel.background){
38576             this.setActivePanel(panel);
38577             if(this.config.initialSize && this.panels.getCount()==1){
38578                 this.resizeTo(this.config.initialSize);
38579             }
38580         }
38581         this.fireEvent("paneladded", this, panel);
38582         return panel;
38583     },
38584     
38585     /**
38586      * Returns true if the panel is in this region.
38587      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38588      * @return {Boolean}
38589      */
38590     hasPanel : function(panel){
38591         if(typeof panel == "object"){ // must be panel obj
38592             panel = panel.getId();
38593         }
38594         return this.getPanel(panel) ? true : false;
38595     },
38596     
38597     /**
38598      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38599      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38600      * @param {Boolean} preservePanel Overrides the config preservePanel option
38601      * @return {Roo.ContentPanel} The panel that was removed
38602      */
38603     remove : function(panel, preservePanel){
38604         panel = this.getPanel(panel);
38605         if(!panel){
38606             return null;
38607         }
38608         var e = {};
38609         this.fireEvent("beforeremove", this, panel, e);
38610         if(e.cancel === true){
38611             return null;
38612         }
38613         var panelId = panel.getId();
38614         this.panels.removeKey(panelId);
38615         return panel;
38616     },
38617     
38618     /**
38619      * Returns the panel specified or null if it's not in this region.
38620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38621      * @return {Roo.ContentPanel}
38622      */
38623     getPanel : function(id){
38624         if(typeof id == "object"){ // must be panel obj
38625             return id;
38626         }
38627         return this.panels.get(id);
38628     },
38629     
38630     /**
38631      * Returns this regions position (north/south/east/west/center).
38632      * @return {String} 
38633      */
38634     getPosition: function(){
38635         return this.position;    
38636     }
38637 });/*
38638  * Based on:
38639  * Ext JS Library 1.1.1
38640  * Copyright(c) 2006-2007, Ext JS, LLC.
38641  *
38642  * Originally Released Under LGPL - original licence link has changed is not relivant.
38643  *
38644  * Fork - LGPL
38645  * <script type="text/javascript">
38646  */
38647  
38648 /**
38649  * @class Roo.bootstrap.layout.Region
38650  * @extends Roo.bootstrap.layout.Basic
38651  * This class represents a region in a layout manager.
38652  
38653  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38654  * @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})
38655  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38656  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38657  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38658  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38659  * @cfg {String}    title           The title for the region (overrides panel titles)
38660  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38661  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38662  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38663  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38664  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38665  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38666  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38667  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38668  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38669  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38670
38671  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38672  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38673  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38674  * @cfg {Number}    width           For East/West panels
38675  * @cfg {Number}    height          For North/South panels
38676  * @cfg {Boolean}   split           To show the splitter
38677  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38678  * 
38679  * @cfg {string}   cls             Extra CSS classes to add to region
38680  * 
38681  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38682  * @cfg {string}   region  the region that it inhabits..
38683  *
38684
38685  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38686  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38687
38688  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38689  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38690  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38691  */
38692 Roo.bootstrap.layout.Region = function(config)
38693 {
38694     this.applyConfig(config);
38695
38696     var mgr = config.mgr;
38697     var pos = config.region;
38698     config.skipConfig = true;
38699     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38700     
38701     if (mgr.el) {
38702         this.onRender(mgr.el);   
38703     }
38704      
38705     this.visible = true;
38706     this.collapsed = false;
38707     this.unrendered_panels = [];
38708 };
38709
38710 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38711
38712     position: '', // set by wrapper (eg. north/south etc..)
38713     unrendered_panels : null,  // unrendered panels.
38714     
38715     tabPosition : false,
38716     
38717     mgr: false, // points to 'Border'
38718     
38719     
38720     createBody : function(){
38721         /** This region's body element 
38722         * @type Roo.Element */
38723         this.bodyEl = this.el.createChild({
38724                 tag: "div",
38725                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38726         });
38727     },
38728
38729     onRender: function(ctr, pos)
38730     {
38731         var dh = Roo.DomHelper;
38732         /** This region's container element 
38733         * @type Roo.Element */
38734         this.el = dh.append(ctr.dom, {
38735                 tag: "div",
38736                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38737             }, true);
38738         /** This region's title element 
38739         * @type Roo.Element */
38740     
38741         this.titleEl = dh.append(this.el.dom,  {
38742                 tag: "div",
38743                 unselectable: "on",
38744                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38745                 children:[
38746                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38747                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38748                 ]
38749             }, true);
38750         
38751         this.titleEl.enableDisplayMode();
38752         /** This region's title text element 
38753         * @type HTMLElement */
38754         this.titleTextEl = this.titleEl.dom.firstChild;
38755         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38756         /*
38757         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38758         this.closeBtn.enableDisplayMode();
38759         this.closeBtn.on("click", this.closeClicked, this);
38760         this.closeBtn.hide();
38761     */
38762         this.createBody(this.config);
38763         if(this.config.hideWhenEmpty){
38764             this.hide();
38765             this.on("paneladded", this.validateVisibility, this);
38766             this.on("panelremoved", this.validateVisibility, this);
38767         }
38768         if(this.autoScroll){
38769             this.bodyEl.setStyle("overflow", "auto");
38770         }else{
38771             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38772         }
38773         //if(c.titlebar !== false){
38774             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38775                 this.titleEl.hide();
38776             }else{
38777                 this.titleEl.show();
38778                 if(this.config.title){
38779                     this.titleTextEl.innerHTML = this.config.title;
38780                 }
38781             }
38782         //}
38783         if(this.config.collapsed){
38784             this.collapse(true);
38785         }
38786         if(this.config.hidden){
38787             this.hide();
38788         }
38789         
38790         if (this.unrendered_panels && this.unrendered_panels.length) {
38791             for (var i =0;i< this.unrendered_panels.length; i++) {
38792                 this.add(this.unrendered_panels[i]);
38793             }
38794             this.unrendered_panels = null;
38795             
38796         }
38797         
38798     },
38799     
38800     applyConfig : function(c)
38801     {
38802         /*
38803          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38804             var dh = Roo.DomHelper;
38805             if(c.titlebar !== false){
38806                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38807                 this.collapseBtn.on("click", this.collapse, this);
38808                 this.collapseBtn.enableDisplayMode();
38809                 /*
38810                 if(c.showPin === true || this.showPin){
38811                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38812                     this.stickBtn.enableDisplayMode();
38813                     this.stickBtn.on("click", this.expand, this);
38814                     this.stickBtn.hide();
38815                 }
38816                 
38817             }
38818             */
38819             /** This region's collapsed element
38820             * @type Roo.Element */
38821             /*
38822              *
38823             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38824                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38825             ]}, true);
38826             
38827             if(c.floatable !== false){
38828                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38829                this.collapsedEl.on("click", this.collapseClick, this);
38830             }
38831
38832             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38833                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38834                    id: "message", unselectable: "on", style:{"float":"left"}});
38835                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38836              }
38837             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38838             this.expandBtn.on("click", this.expand, this);
38839             
38840         }
38841         
38842         if(this.collapseBtn){
38843             this.collapseBtn.setVisible(c.collapsible == true);
38844         }
38845         
38846         this.cmargins = c.cmargins || this.cmargins ||
38847                          (this.position == "west" || this.position == "east" ?
38848                              {top: 0, left: 2, right:2, bottom: 0} :
38849                              {top: 2, left: 0, right:0, bottom: 2});
38850         */
38851         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38852         
38853         
38854         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38855         
38856         this.autoScroll = c.autoScroll || false;
38857         
38858         
38859        
38860         
38861         this.duration = c.duration || .30;
38862         this.slideDuration = c.slideDuration || .45;
38863         this.config = c;
38864        
38865     },
38866     /**
38867      * Returns true if this region is currently visible.
38868      * @return {Boolean}
38869      */
38870     isVisible : function(){
38871         return this.visible;
38872     },
38873
38874     /**
38875      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38876      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38877      */
38878     //setCollapsedTitle : function(title){
38879     //    title = title || "&#160;";
38880      //   if(this.collapsedTitleTextEl){
38881       //      this.collapsedTitleTextEl.innerHTML = title;
38882        // }
38883     //},
38884
38885     getBox : function(){
38886         var b;
38887       //  if(!this.collapsed){
38888             b = this.el.getBox(false, true);
38889        // }else{
38890           //  b = this.collapsedEl.getBox(false, true);
38891         //}
38892         return b;
38893     },
38894
38895     getMargins : function(){
38896         return this.margins;
38897         //return this.collapsed ? this.cmargins : this.margins;
38898     },
38899 /*
38900     highlight : function(){
38901         this.el.addClass("x-layout-panel-dragover");
38902     },
38903
38904     unhighlight : function(){
38905         this.el.removeClass("x-layout-panel-dragover");
38906     },
38907 */
38908     updateBox : function(box)
38909     {
38910         if (!this.bodyEl) {
38911             return; // not rendered yet..
38912         }
38913         
38914         this.box = box;
38915         if(!this.collapsed){
38916             this.el.dom.style.left = box.x + "px";
38917             this.el.dom.style.top = box.y + "px";
38918             this.updateBody(box.width, box.height);
38919         }else{
38920             this.collapsedEl.dom.style.left = box.x + "px";
38921             this.collapsedEl.dom.style.top = box.y + "px";
38922             this.collapsedEl.setSize(box.width, box.height);
38923         }
38924         if(this.tabs){
38925             this.tabs.autoSizeTabs();
38926         }
38927     },
38928
38929     updateBody : function(w, h)
38930     {
38931         if(w !== null){
38932             this.el.setWidth(w);
38933             w -= this.el.getBorderWidth("rl");
38934             if(this.config.adjustments){
38935                 w += this.config.adjustments[0];
38936             }
38937         }
38938         if(h !== null && h > 0){
38939             this.el.setHeight(h);
38940             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38941             h -= this.el.getBorderWidth("tb");
38942             if(this.config.adjustments){
38943                 h += this.config.adjustments[1];
38944             }
38945             this.bodyEl.setHeight(h);
38946             if(this.tabs){
38947                 h = this.tabs.syncHeight(h);
38948             }
38949         }
38950         if(this.panelSize){
38951             w = w !== null ? w : this.panelSize.width;
38952             h = h !== null ? h : this.panelSize.height;
38953         }
38954         if(this.activePanel){
38955             var el = this.activePanel.getEl();
38956             w = w !== null ? w : el.getWidth();
38957             h = h !== null ? h : el.getHeight();
38958             this.panelSize = {width: w, height: h};
38959             this.activePanel.setSize(w, h);
38960         }
38961         if(Roo.isIE && this.tabs){
38962             this.tabs.el.repaint();
38963         }
38964     },
38965
38966     /**
38967      * Returns the container element for this region.
38968      * @return {Roo.Element}
38969      */
38970     getEl : function(){
38971         return this.el;
38972     },
38973
38974     /**
38975      * Hides this region.
38976      */
38977     hide : function(){
38978         //if(!this.collapsed){
38979             this.el.dom.style.left = "-2000px";
38980             this.el.hide();
38981         //}else{
38982          //   this.collapsedEl.dom.style.left = "-2000px";
38983          //   this.collapsedEl.hide();
38984        // }
38985         this.visible = false;
38986         this.fireEvent("visibilitychange", this, false);
38987     },
38988
38989     /**
38990      * Shows this region if it was previously hidden.
38991      */
38992     show : function(){
38993         //if(!this.collapsed){
38994             this.el.show();
38995         //}else{
38996         //    this.collapsedEl.show();
38997        // }
38998         this.visible = true;
38999         this.fireEvent("visibilitychange", this, true);
39000     },
39001 /*
39002     closeClicked : function(){
39003         if(this.activePanel){
39004             this.remove(this.activePanel);
39005         }
39006     },
39007
39008     collapseClick : function(e){
39009         if(this.isSlid){
39010            e.stopPropagation();
39011            this.slideIn();
39012         }else{
39013            e.stopPropagation();
39014            this.slideOut();
39015         }
39016     },
39017 */
39018     /**
39019      * Collapses this region.
39020      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39021      */
39022     /*
39023     collapse : function(skipAnim, skipCheck = false){
39024         if(this.collapsed) {
39025             return;
39026         }
39027         
39028         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39029             
39030             this.collapsed = true;
39031             if(this.split){
39032                 this.split.el.hide();
39033             }
39034             if(this.config.animate && skipAnim !== true){
39035                 this.fireEvent("invalidated", this);
39036                 this.animateCollapse();
39037             }else{
39038                 this.el.setLocation(-20000,-20000);
39039                 this.el.hide();
39040                 this.collapsedEl.show();
39041                 this.fireEvent("collapsed", this);
39042                 this.fireEvent("invalidated", this);
39043             }
39044         }
39045         
39046     },
39047 */
39048     animateCollapse : function(){
39049         // overridden
39050     },
39051
39052     /**
39053      * Expands this region if it was previously collapsed.
39054      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39055      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39056      */
39057     /*
39058     expand : function(e, skipAnim){
39059         if(e) {
39060             e.stopPropagation();
39061         }
39062         if(!this.collapsed || this.el.hasActiveFx()) {
39063             return;
39064         }
39065         if(this.isSlid){
39066             this.afterSlideIn();
39067             skipAnim = true;
39068         }
39069         this.collapsed = false;
39070         if(this.config.animate && skipAnim !== true){
39071             this.animateExpand();
39072         }else{
39073             this.el.show();
39074             if(this.split){
39075                 this.split.el.show();
39076             }
39077             this.collapsedEl.setLocation(-2000,-2000);
39078             this.collapsedEl.hide();
39079             this.fireEvent("invalidated", this);
39080             this.fireEvent("expanded", this);
39081         }
39082     },
39083 */
39084     animateExpand : function(){
39085         // overridden
39086     },
39087
39088     initTabs : function()
39089     {
39090         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39091         
39092         var ts = new Roo.bootstrap.panel.Tabs({
39093             el: this.bodyEl.dom,
39094             region : this,
39095             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39096             disableTooltips: this.config.disableTabTips,
39097             toolbar : this.config.toolbar
39098         });
39099         
39100         if(this.config.hideTabs){
39101             ts.stripWrap.setDisplayed(false);
39102         }
39103         this.tabs = ts;
39104         ts.resizeTabs = this.config.resizeTabs === true;
39105         ts.minTabWidth = this.config.minTabWidth || 40;
39106         ts.maxTabWidth = this.config.maxTabWidth || 250;
39107         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39108         ts.monitorResize = false;
39109         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39110         ts.bodyEl.addClass('roo-layout-tabs-body');
39111         this.panels.each(this.initPanelAsTab, this);
39112     },
39113
39114     initPanelAsTab : function(panel){
39115         var ti = this.tabs.addTab(
39116             panel.getEl().id,
39117             panel.getTitle(),
39118             null,
39119             this.config.closeOnTab && panel.isClosable(),
39120             panel.tpl
39121         );
39122         if(panel.tabTip !== undefined){
39123             ti.setTooltip(panel.tabTip);
39124         }
39125         ti.on("activate", function(){
39126               this.setActivePanel(panel);
39127         }, this);
39128         
39129         if(this.config.closeOnTab){
39130             ti.on("beforeclose", function(t, e){
39131                 e.cancel = true;
39132                 this.remove(panel);
39133             }, this);
39134         }
39135         
39136         panel.tabItem = ti;
39137         
39138         return ti;
39139     },
39140
39141     updatePanelTitle : function(panel, title)
39142     {
39143         if(this.activePanel == panel){
39144             this.updateTitle(title);
39145         }
39146         if(this.tabs){
39147             var ti = this.tabs.getTab(panel.getEl().id);
39148             ti.setText(title);
39149             if(panel.tabTip !== undefined){
39150                 ti.setTooltip(panel.tabTip);
39151             }
39152         }
39153     },
39154
39155     updateTitle : function(title){
39156         if(this.titleTextEl && !this.config.title){
39157             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39158         }
39159     },
39160
39161     setActivePanel : function(panel)
39162     {
39163         panel = this.getPanel(panel);
39164         if(this.activePanel && this.activePanel != panel){
39165             if(this.activePanel.setActiveState(false) === false){
39166                 return;
39167             }
39168         }
39169         this.activePanel = panel;
39170         panel.setActiveState(true);
39171         if(this.panelSize){
39172             panel.setSize(this.panelSize.width, this.panelSize.height);
39173         }
39174         if(this.closeBtn){
39175             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39176         }
39177         this.updateTitle(panel.getTitle());
39178         if(this.tabs){
39179             this.fireEvent("invalidated", this);
39180         }
39181         this.fireEvent("panelactivated", this, panel);
39182     },
39183
39184     /**
39185      * Shows the specified panel.
39186      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39187      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39188      */
39189     showPanel : function(panel)
39190     {
39191         panel = this.getPanel(panel);
39192         if(panel){
39193             if(this.tabs){
39194                 var tab = this.tabs.getTab(panel.getEl().id);
39195                 if(tab.isHidden()){
39196                     this.tabs.unhideTab(tab.id);
39197                 }
39198                 tab.activate();
39199             }else{
39200                 this.setActivePanel(panel);
39201             }
39202         }
39203         return panel;
39204     },
39205
39206     /**
39207      * Get the active panel for this region.
39208      * @return {Roo.ContentPanel} The active panel or null
39209      */
39210     getActivePanel : function(){
39211         return this.activePanel;
39212     },
39213
39214     validateVisibility : function(){
39215         if(this.panels.getCount() < 1){
39216             this.updateTitle("&#160;");
39217             this.closeBtn.hide();
39218             this.hide();
39219         }else{
39220             if(!this.isVisible()){
39221                 this.show();
39222             }
39223         }
39224     },
39225
39226     /**
39227      * Adds the passed ContentPanel(s) to this region.
39228      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39229      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39230      */
39231     add : function(panel)
39232     {
39233         if(arguments.length > 1){
39234             for(var i = 0, len = arguments.length; i < len; i++) {
39235                 this.add(arguments[i]);
39236             }
39237             return null;
39238         }
39239         
39240         // if we have not been rendered yet, then we can not really do much of this..
39241         if (!this.bodyEl) {
39242             this.unrendered_panels.push(panel);
39243             return panel;
39244         }
39245         
39246         
39247         
39248         
39249         if(this.hasPanel(panel)){
39250             this.showPanel(panel);
39251             return panel;
39252         }
39253         panel.setRegion(this);
39254         this.panels.add(panel);
39255        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39256             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39257             // and hide them... ???
39258             this.bodyEl.dom.appendChild(panel.getEl().dom);
39259             if(panel.background !== true){
39260                 this.setActivePanel(panel);
39261             }
39262             this.fireEvent("paneladded", this, panel);
39263             return panel;
39264         }
39265         */
39266         if(!this.tabs){
39267             this.initTabs();
39268         }else{
39269             this.initPanelAsTab(panel);
39270         }
39271         
39272         
39273         if(panel.background !== true){
39274             this.tabs.activate(panel.getEl().id);
39275         }
39276         this.fireEvent("paneladded", this, panel);
39277         return panel;
39278     },
39279
39280     /**
39281      * Hides the tab for the specified panel.
39282      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39283      */
39284     hidePanel : function(panel){
39285         if(this.tabs && (panel = this.getPanel(panel))){
39286             this.tabs.hideTab(panel.getEl().id);
39287         }
39288     },
39289
39290     /**
39291      * Unhides the tab for a previously hidden panel.
39292      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39293      */
39294     unhidePanel : function(panel){
39295         if(this.tabs && (panel = this.getPanel(panel))){
39296             this.tabs.unhideTab(panel.getEl().id);
39297         }
39298     },
39299
39300     clearPanels : function(){
39301         while(this.panels.getCount() > 0){
39302              this.remove(this.panels.first());
39303         }
39304     },
39305
39306     /**
39307      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39308      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39309      * @param {Boolean} preservePanel Overrides the config preservePanel option
39310      * @return {Roo.ContentPanel} The panel that was removed
39311      */
39312     remove : function(panel, preservePanel)
39313     {
39314         panel = this.getPanel(panel);
39315         if(!panel){
39316             return null;
39317         }
39318         var e = {};
39319         this.fireEvent("beforeremove", this, panel, e);
39320         if(e.cancel === true){
39321             return null;
39322         }
39323         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39324         var panelId = panel.getId();
39325         this.panels.removeKey(panelId);
39326         if(preservePanel){
39327             document.body.appendChild(panel.getEl().dom);
39328         }
39329         if(this.tabs){
39330             this.tabs.removeTab(panel.getEl().id);
39331         }else if (!preservePanel){
39332             this.bodyEl.dom.removeChild(panel.getEl().dom);
39333         }
39334         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39335             var p = this.panels.first();
39336             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39337             tempEl.appendChild(p.getEl().dom);
39338             this.bodyEl.update("");
39339             this.bodyEl.dom.appendChild(p.getEl().dom);
39340             tempEl = null;
39341             this.updateTitle(p.getTitle());
39342             this.tabs = null;
39343             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39344             this.setActivePanel(p);
39345         }
39346         panel.setRegion(null);
39347         if(this.activePanel == panel){
39348             this.activePanel = null;
39349         }
39350         if(this.config.autoDestroy !== false && preservePanel !== true){
39351             try{panel.destroy();}catch(e){}
39352         }
39353         this.fireEvent("panelremoved", this, panel);
39354         return panel;
39355     },
39356
39357     /**
39358      * Returns the TabPanel component used by this region
39359      * @return {Roo.TabPanel}
39360      */
39361     getTabs : function(){
39362         return this.tabs;
39363     },
39364
39365     createTool : function(parentEl, className){
39366         var btn = Roo.DomHelper.append(parentEl, {
39367             tag: "div",
39368             cls: "x-layout-tools-button",
39369             children: [ {
39370                 tag: "div",
39371                 cls: "roo-layout-tools-button-inner " + className,
39372                 html: "&#160;"
39373             }]
39374         }, true);
39375         btn.addClassOnOver("roo-layout-tools-button-over");
39376         return btn;
39377     }
39378 });/*
39379  * Based on:
39380  * Ext JS Library 1.1.1
39381  * Copyright(c) 2006-2007, Ext JS, LLC.
39382  *
39383  * Originally Released Under LGPL - original licence link has changed is not relivant.
39384  *
39385  * Fork - LGPL
39386  * <script type="text/javascript">
39387  */
39388  
39389
39390
39391 /**
39392  * @class Roo.SplitLayoutRegion
39393  * @extends Roo.LayoutRegion
39394  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39395  */
39396 Roo.bootstrap.layout.Split = function(config){
39397     this.cursor = config.cursor;
39398     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39399 };
39400
39401 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39402 {
39403     splitTip : "Drag to resize.",
39404     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39405     useSplitTips : false,
39406
39407     applyConfig : function(config){
39408         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39409     },
39410     
39411     onRender : function(ctr,pos) {
39412         
39413         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39414         if(!this.config.split){
39415             return;
39416         }
39417         if(!this.split){
39418             
39419             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39420                             tag: "div",
39421                             id: this.el.id + "-split",
39422                             cls: "roo-layout-split roo-layout-split-"+this.position,
39423                             html: "&#160;"
39424             });
39425             /** The SplitBar for this region 
39426             * @type Roo.SplitBar */
39427             // does not exist yet...
39428             Roo.log([this.position, this.orientation]);
39429             
39430             this.split = new Roo.bootstrap.SplitBar({
39431                 dragElement : splitEl,
39432                 resizingElement: this.el,
39433                 orientation : this.orientation
39434             });
39435             
39436             this.split.on("moved", this.onSplitMove, this);
39437             this.split.useShim = this.config.useShim === true;
39438             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39439             if(this.useSplitTips){
39440                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39441             }
39442             //if(config.collapsible){
39443             //    this.split.el.on("dblclick", this.collapse,  this);
39444             //}
39445         }
39446         if(typeof this.config.minSize != "undefined"){
39447             this.split.minSize = this.config.minSize;
39448         }
39449         if(typeof this.config.maxSize != "undefined"){
39450             this.split.maxSize = this.config.maxSize;
39451         }
39452         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39453             this.hideSplitter();
39454         }
39455         
39456     },
39457
39458     getHMaxSize : function(){
39459          var cmax = this.config.maxSize || 10000;
39460          var center = this.mgr.getRegion("center");
39461          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39462     },
39463
39464     getVMaxSize : function(){
39465          var cmax = this.config.maxSize || 10000;
39466          var center = this.mgr.getRegion("center");
39467          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39468     },
39469
39470     onSplitMove : function(split, newSize){
39471         this.fireEvent("resized", this, newSize);
39472     },
39473     
39474     /** 
39475      * Returns the {@link Roo.SplitBar} for this region.
39476      * @return {Roo.SplitBar}
39477      */
39478     getSplitBar : function(){
39479         return this.split;
39480     },
39481     
39482     hide : function(){
39483         this.hideSplitter();
39484         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39485     },
39486
39487     hideSplitter : function(){
39488         if(this.split){
39489             this.split.el.setLocation(-2000,-2000);
39490             this.split.el.hide();
39491         }
39492     },
39493
39494     show : function(){
39495         if(this.split){
39496             this.split.el.show();
39497         }
39498         Roo.bootstrap.layout.Split.superclass.show.call(this);
39499     },
39500     
39501     beforeSlide: function(){
39502         if(Roo.isGecko){// firefox overflow auto bug workaround
39503             this.bodyEl.clip();
39504             if(this.tabs) {
39505                 this.tabs.bodyEl.clip();
39506             }
39507             if(this.activePanel){
39508                 this.activePanel.getEl().clip();
39509                 
39510                 if(this.activePanel.beforeSlide){
39511                     this.activePanel.beforeSlide();
39512                 }
39513             }
39514         }
39515     },
39516     
39517     afterSlide : function(){
39518         if(Roo.isGecko){// firefox overflow auto bug workaround
39519             this.bodyEl.unclip();
39520             if(this.tabs) {
39521                 this.tabs.bodyEl.unclip();
39522             }
39523             if(this.activePanel){
39524                 this.activePanel.getEl().unclip();
39525                 if(this.activePanel.afterSlide){
39526                     this.activePanel.afterSlide();
39527                 }
39528             }
39529         }
39530     },
39531
39532     initAutoHide : function(){
39533         if(this.autoHide !== false){
39534             if(!this.autoHideHd){
39535                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39536                 this.autoHideHd = {
39537                     "mouseout": function(e){
39538                         if(!e.within(this.el, true)){
39539                             st.delay(500);
39540                         }
39541                     },
39542                     "mouseover" : function(e){
39543                         st.cancel();
39544                     },
39545                     scope : this
39546                 };
39547             }
39548             this.el.on(this.autoHideHd);
39549         }
39550     },
39551
39552     clearAutoHide : function(){
39553         if(this.autoHide !== false){
39554             this.el.un("mouseout", this.autoHideHd.mouseout);
39555             this.el.un("mouseover", this.autoHideHd.mouseover);
39556         }
39557     },
39558
39559     clearMonitor : function(){
39560         Roo.get(document).un("click", this.slideInIf, this);
39561     },
39562
39563     // these names are backwards but not changed for compat
39564     slideOut : function(){
39565         if(this.isSlid || this.el.hasActiveFx()){
39566             return;
39567         }
39568         this.isSlid = true;
39569         if(this.collapseBtn){
39570             this.collapseBtn.hide();
39571         }
39572         this.closeBtnState = this.closeBtn.getStyle('display');
39573         this.closeBtn.hide();
39574         if(this.stickBtn){
39575             this.stickBtn.show();
39576         }
39577         this.el.show();
39578         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39579         this.beforeSlide();
39580         this.el.setStyle("z-index", 10001);
39581         this.el.slideIn(this.getSlideAnchor(), {
39582             callback: function(){
39583                 this.afterSlide();
39584                 this.initAutoHide();
39585                 Roo.get(document).on("click", this.slideInIf, this);
39586                 this.fireEvent("slideshow", this);
39587             },
39588             scope: this,
39589             block: true
39590         });
39591     },
39592
39593     afterSlideIn : function(){
39594         this.clearAutoHide();
39595         this.isSlid = false;
39596         this.clearMonitor();
39597         this.el.setStyle("z-index", "");
39598         if(this.collapseBtn){
39599             this.collapseBtn.show();
39600         }
39601         this.closeBtn.setStyle('display', this.closeBtnState);
39602         if(this.stickBtn){
39603             this.stickBtn.hide();
39604         }
39605         this.fireEvent("slidehide", this);
39606     },
39607
39608     slideIn : function(cb){
39609         if(!this.isSlid || this.el.hasActiveFx()){
39610             Roo.callback(cb);
39611             return;
39612         }
39613         this.isSlid = false;
39614         this.beforeSlide();
39615         this.el.slideOut(this.getSlideAnchor(), {
39616             callback: function(){
39617                 this.el.setLeftTop(-10000, -10000);
39618                 this.afterSlide();
39619                 this.afterSlideIn();
39620                 Roo.callback(cb);
39621             },
39622             scope: this,
39623             block: true
39624         });
39625     },
39626     
39627     slideInIf : function(e){
39628         if(!e.within(this.el)){
39629             this.slideIn();
39630         }
39631     },
39632
39633     animateCollapse : function(){
39634         this.beforeSlide();
39635         this.el.setStyle("z-index", 20000);
39636         var anchor = this.getSlideAnchor();
39637         this.el.slideOut(anchor, {
39638             callback : function(){
39639                 this.el.setStyle("z-index", "");
39640                 this.collapsedEl.slideIn(anchor, {duration:.3});
39641                 this.afterSlide();
39642                 this.el.setLocation(-10000,-10000);
39643                 this.el.hide();
39644                 this.fireEvent("collapsed", this);
39645             },
39646             scope: this,
39647             block: true
39648         });
39649     },
39650
39651     animateExpand : function(){
39652         this.beforeSlide();
39653         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39654         this.el.setStyle("z-index", 20000);
39655         this.collapsedEl.hide({
39656             duration:.1
39657         });
39658         this.el.slideIn(this.getSlideAnchor(), {
39659             callback : function(){
39660                 this.el.setStyle("z-index", "");
39661                 this.afterSlide();
39662                 if(this.split){
39663                     this.split.el.show();
39664                 }
39665                 this.fireEvent("invalidated", this);
39666                 this.fireEvent("expanded", this);
39667             },
39668             scope: this,
39669             block: true
39670         });
39671     },
39672
39673     anchors : {
39674         "west" : "left",
39675         "east" : "right",
39676         "north" : "top",
39677         "south" : "bottom"
39678     },
39679
39680     sanchors : {
39681         "west" : "l",
39682         "east" : "r",
39683         "north" : "t",
39684         "south" : "b"
39685     },
39686
39687     canchors : {
39688         "west" : "tl-tr",
39689         "east" : "tr-tl",
39690         "north" : "tl-bl",
39691         "south" : "bl-tl"
39692     },
39693
39694     getAnchor : function(){
39695         return this.anchors[this.position];
39696     },
39697
39698     getCollapseAnchor : function(){
39699         return this.canchors[this.position];
39700     },
39701
39702     getSlideAnchor : function(){
39703         return this.sanchors[this.position];
39704     },
39705
39706     getAlignAdj : function(){
39707         var cm = this.cmargins;
39708         switch(this.position){
39709             case "west":
39710                 return [0, 0];
39711             break;
39712             case "east":
39713                 return [0, 0];
39714             break;
39715             case "north":
39716                 return [0, 0];
39717             break;
39718             case "south":
39719                 return [0, 0];
39720             break;
39721         }
39722     },
39723
39724     getExpandAdj : function(){
39725         var c = this.collapsedEl, cm = this.cmargins;
39726         switch(this.position){
39727             case "west":
39728                 return [-(cm.right+c.getWidth()+cm.left), 0];
39729             break;
39730             case "east":
39731                 return [cm.right+c.getWidth()+cm.left, 0];
39732             break;
39733             case "north":
39734                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39735             break;
39736             case "south":
39737                 return [0, cm.top+cm.bottom+c.getHeight()];
39738             break;
39739         }
39740     }
39741 });/*
39742  * Based on:
39743  * Ext JS Library 1.1.1
39744  * Copyright(c) 2006-2007, Ext JS, LLC.
39745  *
39746  * Originally Released Under LGPL - original licence link has changed is not relivant.
39747  *
39748  * Fork - LGPL
39749  * <script type="text/javascript">
39750  */
39751 /*
39752  * These classes are private internal classes
39753  */
39754 Roo.bootstrap.layout.Center = function(config){
39755     config.region = "center";
39756     Roo.bootstrap.layout.Region.call(this, config);
39757     this.visible = true;
39758     this.minWidth = config.minWidth || 20;
39759     this.minHeight = config.minHeight || 20;
39760 };
39761
39762 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39763     hide : function(){
39764         // center panel can't be hidden
39765     },
39766     
39767     show : function(){
39768         // center panel can't be hidden
39769     },
39770     
39771     getMinWidth: function(){
39772         return this.minWidth;
39773     },
39774     
39775     getMinHeight: function(){
39776         return this.minHeight;
39777     }
39778 });
39779
39780
39781
39782
39783  
39784
39785
39786
39787
39788
39789
39790 Roo.bootstrap.layout.North = function(config)
39791 {
39792     config.region = 'north';
39793     config.cursor = 'n-resize';
39794     
39795     Roo.bootstrap.layout.Split.call(this, config);
39796     
39797     
39798     if(this.split){
39799         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39800         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39801         this.split.el.addClass("roo-layout-split-v");
39802     }
39803     //var size = config.initialSize || config.height;
39804     //if(this.el && typeof size != "undefined"){
39805     //    this.el.setHeight(size);
39806     //}
39807 };
39808 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39809 {
39810     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39811      
39812      
39813     onRender : function(ctr, pos)
39814     {
39815         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39816         var size = this.config.initialSize || this.config.height;
39817         if(this.el && typeof size != "undefined"){
39818             this.el.setHeight(size);
39819         }
39820     
39821     },
39822     
39823     getBox : function(){
39824         if(this.collapsed){
39825             return this.collapsedEl.getBox();
39826         }
39827         var box = this.el.getBox();
39828         if(this.split){
39829             box.height += this.split.el.getHeight();
39830         }
39831         return box;
39832     },
39833     
39834     updateBox : function(box){
39835         if(this.split && !this.collapsed){
39836             box.height -= this.split.el.getHeight();
39837             this.split.el.setLeft(box.x);
39838             this.split.el.setTop(box.y+box.height);
39839             this.split.el.setWidth(box.width);
39840         }
39841         if(this.collapsed){
39842             this.updateBody(box.width, null);
39843         }
39844         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39845     }
39846 });
39847
39848
39849
39850
39851
39852 Roo.bootstrap.layout.South = function(config){
39853     config.region = 'south';
39854     config.cursor = 's-resize';
39855     Roo.bootstrap.layout.Split.call(this, config);
39856     if(this.split){
39857         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39858         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39859         this.split.el.addClass("roo-layout-split-v");
39860     }
39861     
39862 };
39863
39864 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39865     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39866     
39867     onRender : function(ctr, pos)
39868     {
39869         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39870         var size = this.config.initialSize || this.config.height;
39871         if(this.el && typeof size != "undefined"){
39872             this.el.setHeight(size);
39873         }
39874     
39875     },
39876     
39877     getBox : function(){
39878         if(this.collapsed){
39879             return this.collapsedEl.getBox();
39880         }
39881         var box = this.el.getBox();
39882         if(this.split){
39883             var sh = this.split.el.getHeight();
39884             box.height += sh;
39885             box.y -= sh;
39886         }
39887         return box;
39888     },
39889     
39890     updateBox : function(box){
39891         if(this.split && !this.collapsed){
39892             var sh = this.split.el.getHeight();
39893             box.height -= sh;
39894             box.y += sh;
39895             this.split.el.setLeft(box.x);
39896             this.split.el.setTop(box.y-sh);
39897             this.split.el.setWidth(box.width);
39898         }
39899         if(this.collapsed){
39900             this.updateBody(box.width, null);
39901         }
39902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39903     }
39904 });
39905
39906 Roo.bootstrap.layout.East = function(config){
39907     config.region = "east";
39908     config.cursor = "e-resize";
39909     Roo.bootstrap.layout.Split.call(this, config);
39910     if(this.split){
39911         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39912         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39913         this.split.el.addClass("roo-layout-split-h");
39914     }
39915     
39916 };
39917 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39918     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39919     
39920     onRender : function(ctr, pos)
39921     {
39922         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39923         var size = this.config.initialSize || this.config.width;
39924         if(this.el && typeof size != "undefined"){
39925             this.el.setWidth(size);
39926         }
39927     
39928     },
39929     
39930     getBox : function(){
39931         if(this.collapsed){
39932             return this.collapsedEl.getBox();
39933         }
39934         var box = this.el.getBox();
39935         if(this.split){
39936             var sw = this.split.el.getWidth();
39937             box.width += sw;
39938             box.x -= sw;
39939         }
39940         return box;
39941     },
39942
39943     updateBox : function(box){
39944         if(this.split && !this.collapsed){
39945             var sw = this.split.el.getWidth();
39946             box.width -= sw;
39947             this.split.el.setLeft(box.x);
39948             this.split.el.setTop(box.y);
39949             this.split.el.setHeight(box.height);
39950             box.x += sw;
39951         }
39952         if(this.collapsed){
39953             this.updateBody(null, box.height);
39954         }
39955         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39956     }
39957 });
39958
39959 Roo.bootstrap.layout.West = function(config){
39960     config.region = "west";
39961     config.cursor = "w-resize";
39962     
39963     Roo.bootstrap.layout.Split.call(this, config);
39964     if(this.split){
39965         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39966         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39967         this.split.el.addClass("roo-layout-split-h");
39968     }
39969     
39970 };
39971 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39972     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39973     
39974     onRender: function(ctr, pos)
39975     {
39976         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39977         var size = this.config.initialSize || this.config.width;
39978         if(typeof size != "undefined"){
39979             this.el.setWidth(size);
39980         }
39981     },
39982     
39983     getBox : function(){
39984         if(this.collapsed){
39985             return this.collapsedEl.getBox();
39986         }
39987         var box = this.el.getBox();
39988         if (box.width == 0) {
39989             box.width = this.config.width; // kludge?
39990         }
39991         if(this.split){
39992             box.width += this.split.el.getWidth();
39993         }
39994         return box;
39995     },
39996     
39997     updateBox : function(box){
39998         if(this.split && !this.collapsed){
39999             var sw = this.split.el.getWidth();
40000             box.width -= sw;
40001             this.split.el.setLeft(box.x+box.width);
40002             this.split.el.setTop(box.y);
40003             this.split.el.setHeight(box.height);
40004         }
40005         if(this.collapsed){
40006             this.updateBody(null, box.height);
40007         }
40008         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40009     }
40010 });Roo.namespace("Roo.bootstrap.panel");/*
40011  * Based on:
40012  * Ext JS Library 1.1.1
40013  * Copyright(c) 2006-2007, Ext JS, LLC.
40014  *
40015  * Originally Released Under LGPL - original licence link has changed is not relivant.
40016  *
40017  * Fork - LGPL
40018  * <script type="text/javascript">
40019  */
40020 /**
40021  * @class Roo.ContentPanel
40022  * @extends Roo.util.Observable
40023  * A basic ContentPanel element.
40024  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40025  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40026  * @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
40027  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40028  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40029  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40030  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40031  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40032  * @cfg {String} title          The title for this panel
40033  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40034  * @cfg {String} url            Calls {@link #setUrl} with this value
40035  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40036  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40037  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40038  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40039  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40040  * @cfg {Boolean} badges render the badges
40041  * @cfg {String} cls  extra classes to use  
40042  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40043
40044  * @constructor
40045  * Create a new ContentPanel.
40046  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40047  * @param {String/Object} config A string to set only the title or a config object
40048  * @param {String} content (optional) Set the HTML content for this panel
40049  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40050  */
40051 Roo.bootstrap.panel.Content = function( config){
40052     
40053     this.tpl = config.tpl || false;
40054     
40055     var el = config.el;
40056     var content = config.content;
40057
40058     if(config.autoCreate){ // xtype is available if this is called from factory
40059         el = Roo.id();
40060     }
40061     this.el = Roo.get(el);
40062     if(!this.el && config && config.autoCreate){
40063         if(typeof config.autoCreate == "object"){
40064             if(!config.autoCreate.id){
40065                 config.autoCreate.id = config.id||el;
40066             }
40067             this.el = Roo.DomHelper.append(document.body,
40068                         config.autoCreate, true);
40069         }else{
40070             var elcfg =  {
40071                 tag: "div",
40072                 cls: (config.cls || '') +
40073                     (config.background ? ' bg-' + config.background : '') +
40074                     " roo-layout-inactive-content",
40075                 id: config.id||el
40076             };
40077             if (config.iframe) {
40078                 elcfg.cn = [
40079                     {
40080                         tag : 'iframe',
40081                         style : 'border: 0px',
40082                         src : 'about:blank'
40083                     }
40084                 ];
40085             }
40086               
40087             if (config.html) {
40088                 elcfg.html = config.html;
40089                 
40090             }
40091                         
40092             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40093             if (config.iframe) {
40094                 this.iframeEl = this.el.select('iframe',true).first();
40095             }
40096             
40097         }
40098     } 
40099     this.closable = false;
40100     this.loaded = false;
40101     this.active = false;
40102    
40103       
40104     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40105         
40106         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40107         
40108         this.wrapEl = this.el; //this.el.wrap();
40109         var ti = [];
40110         if (config.toolbar.items) {
40111             ti = config.toolbar.items ;
40112             delete config.toolbar.items ;
40113         }
40114         
40115         var nitems = [];
40116         this.toolbar.render(this.wrapEl, 'before');
40117         for(var i =0;i < ti.length;i++) {
40118           //  Roo.log(['add child', items[i]]);
40119             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40120         }
40121         this.toolbar.items = nitems;
40122         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40123         delete config.toolbar;
40124         
40125     }
40126     /*
40127     // xtype created footer. - not sure if will work as we normally have to render first..
40128     if (this.footer && !this.footer.el && this.footer.xtype) {
40129         if (!this.wrapEl) {
40130             this.wrapEl = this.el.wrap();
40131         }
40132     
40133         this.footer.container = this.wrapEl.createChild();
40134          
40135         this.footer = Roo.factory(this.footer, Roo);
40136         
40137     }
40138     */
40139     
40140      if(typeof config == "string"){
40141         this.title = config;
40142     }else{
40143         Roo.apply(this, config);
40144     }
40145     
40146     if(this.resizeEl){
40147         this.resizeEl = Roo.get(this.resizeEl, true);
40148     }else{
40149         this.resizeEl = this.el;
40150     }
40151     // handle view.xtype
40152     
40153  
40154     
40155     
40156     this.addEvents({
40157         /**
40158          * @event activate
40159          * Fires when this panel is activated. 
40160          * @param {Roo.ContentPanel} this
40161          */
40162         "activate" : true,
40163         /**
40164          * @event deactivate
40165          * Fires when this panel is activated. 
40166          * @param {Roo.ContentPanel} this
40167          */
40168         "deactivate" : true,
40169
40170         /**
40171          * @event resize
40172          * Fires when this panel is resized if fitToFrame is true.
40173          * @param {Roo.ContentPanel} this
40174          * @param {Number} width The width after any component adjustments
40175          * @param {Number} height The height after any component adjustments
40176          */
40177         "resize" : true,
40178         
40179          /**
40180          * @event render
40181          * Fires when this tab is created
40182          * @param {Roo.ContentPanel} this
40183          */
40184         "render" : true
40185         
40186         
40187         
40188     });
40189     
40190
40191     
40192     
40193     if(this.autoScroll && !this.iframe){
40194         this.resizeEl.setStyle("overflow", "auto");
40195     } else {
40196         // fix randome scrolling
40197         //this.el.on('scroll', function() {
40198         //    Roo.log('fix random scolling');
40199         //    this.scrollTo('top',0); 
40200         //});
40201     }
40202     content = content || this.content;
40203     if(content){
40204         this.setContent(content);
40205     }
40206     if(config && config.url){
40207         this.setUrl(this.url, this.params, this.loadOnce);
40208     }
40209     
40210     
40211     
40212     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40213     
40214     if (this.view && typeof(this.view.xtype) != 'undefined') {
40215         this.view.el = this.el.appendChild(document.createElement("div"));
40216         this.view = Roo.factory(this.view); 
40217         this.view.render  &&  this.view.render(false, '');  
40218     }
40219     
40220     
40221     this.fireEvent('render', this);
40222 };
40223
40224 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40225     
40226     cls : '',
40227     background : '',
40228     
40229     tabTip : '',
40230     
40231     iframe : false,
40232     iframeEl : false,
40233     
40234     setRegion : function(region){
40235         this.region = region;
40236         this.setActiveClass(region && !this.background);
40237     },
40238     
40239     
40240     setActiveClass: function(state)
40241     {
40242         if(state){
40243            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40244            this.el.setStyle('position','relative');
40245         }else{
40246            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40247            this.el.setStyle('position', 'absolute');
40248         } 
40249     },
40250     
40251     /**
40252      * Returns the toolbar for this Panel if one was configured. 
40253      * @return {Roo.Toolbar} 
40254      */
40255     getToolbar : function(){
40256         return this.toolbar;
40257     },
40258     
40259     setActiveState : function(active)
40260     {
40261         this.active = active;
40262         this.setActiveClass(active);
40263         if(!active){
40264             if(this.fireEvent("deactivate", this) === false){
40265                 return false;
40266             }
40267             return true;
40268         }
40269         this.fireEvent("activate", this);
40270         return true;
40271     },
40272     /**
40273      * Updates this panel's element (not for iframe)
40274      * @param {String} content The new content
40275      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40276     */
40277     setContent : function(content, loadScripts){
40278         if (this.iframe) {
40279             return;
40280         }
40281         
40282         this.el.update(content, loadScripts);
40283     },
40284
40285     ignoreResize : function(w, h){
40286         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40287             return true;
40288         }else{
40289             this.lastSize = {width: w, height: h};
40290             return false;
40291         }
40292     },
40293     /**
40294      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40295      * @return {Roo.UpdateManager} The UpdateManager
40296      */
40297     getUpdateManager : function(){
40298         if (this.iframe) {
40299             return false;
40300         }
40301         return this.el.getUpdateManager();
40302     },
40303      /**
40304      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40305      * Does not work with IFRAME contents
40306      * @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:
40307 <pre><code>
40308 panel.load({
40309     url: "your-url.php",
40310     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40311     callback: yourFunction,
40312     scope: yourObject, //(optional scope)
40313     discardUrl: false,
40314     nocache: false,
40315     text: "Loading...",
40316     timeout: 30,
40317     scripts: false
40318 });
40319 </code></pre>
40320      
40321      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40322      * 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.
40323      * @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}
40324      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40325      * @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.
40326      * @return {Roo.ContentPanel} this
40327      */
40328     load : function(){
40329         
40330         if (this.iframe) {
40331             return this;
40332         }
40333         
40334         var um = this.el.getUpdateManager();
40335         um.update.apply(um, arguments);
40336         return this;
40337     },
40338
40339
40340     /**
40341      * 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.
40342      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40343      * @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)
40344      * @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)
40345      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40346      */
40347     setUrl : function(url, params, loadOnce){
40348         if (this.iframe) {
40349             this.iframeEl.dom.src = url;
40350             return false;
40351         }
40352         
40353         if(this.refreshDelegate){
40354             this.removeListener("activate", this.refreshDelegate);
40355         }
40356         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40357         this.on("activate", this.refreshDelegate);
40358         return this.el.getUpdateManager();
40359     },
40360     
40361     _handleRefresh : function(url, params, loadOnce){
40362         if(!loadOnce || !this.loaded){
40363             var updater = this.el.getUpdateManager();
40364             updater.update(url, params, this._setLoaded.createDelegate(this));
40365         }
40366     },
40367     
40368     _setLoaded : function(){
40369         this.loaded = true;
40370     }, 
40371     
40372     /**
40373      * Returns this panel's id
40374      * @return {String} 
40375      */
40376     getId : function(){
40377         return this.el.id;
40378     },
40379     
40380     /** 
40381      * Returns this panel's element - used by regiosn to add.
40382      * @return {Roo.Element} 
40383      */
40384     getEl : function(){
40385         return this.wrapEl || this.el;
40386     },
40387     
40388    
40389     
40390     adjustForComponents : function(width, height)
40391     {
40392         //Roo.log('adjustForComponents ');
40393         if(this.resizeEl != this.el){
40394             width -= this.el.getFrameWidth('lr');
40395             height -= this.el.getFrameWidth('tb');
40396         }
40397         if(this.toolbar){
40398             var te = this.toolbar.getEl();
40399             te.setWidth(width);
40400             height -= te.getHeight();
40401         }
40402         if(this.footer){
40403             var te = this.footer.getEl();
40404             te.setWidth(width);
40405             height -= te.getHeight();
40406         }
40407         
40408         
40409         if(this.adjustments){
40410             width += this.adjustments[0];
40411             height += this.adjustments[1];
40412         }
40413         return {"width": width, "height": height};
40414     },
40415     
40416     setSize : function(width, height){
40417         if(this.fitToFrame && !this.ignoreResize(width, height)){
40418             if(this.fitContainer && this.resizeEl != this.el){
40419                 this.el.setSize(width, height);
40420             }
40421             var size = this.adjustForComponents(width, height);
40422             if (this.iframe) {
40423                 this.iframeEl.setSize(width,height);
40424             }
40425             
40426             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40427             this.fireEvent('resize', this, size.width, size.height);
40428             
40429             
40430         }
40431     },
40432     
40433     /**
40434      * Returns this panel's title
40435      * @return {String} 
40436      */
40437     getTitle : function(){
40438         
40439         if (typeof(this.title) != 'object') {
40440             return this.title;
40441         }
40442         
40443         var t = '';
40444         for (var k in this.title) {
40445             if (!this.title.hasOwnProperty(k)) {
40446                 continue;
40447             }
40448             
40449             if (k.indexOf('-') >= 0) {
40450                 var s = k.split('-');
40451                 for (var i = 0; i<s.length; i++) {
40452                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40453                 }
40454             } else {
40455                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40456             }
40457         }
40458         return t;
40459     },
40460     
40461     /**
40462      * Set this panel's title
40463      * @param {String} title
40464      */
40465     setTitle : function(title){
40466         this.title = title;
40467         if(this.region){
40468             this.region.updatePanelTitle(this, title);
40469         }
40470     },
40471     
40472     /**
40473      * Returns true is this panel was configured to be closable
40474      * @return {Boolean} 
40475      */
40476     isClosable : function(){
40477         return this.closable;
40478     },
40479     
40480     beforeSlide : function(){
40481         this.el.clip();
40482         this.resizeEl.clip();
40483     },
40484     
40485     afterSlide : function(){
40486         this.el.unclip();
40487         this.resizeEl.unclip();
40488     },
40489     
40490     /**
40491      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40492      *   Will fail silently if the {@link #setUrl} method has not been called.
40493      *   This does not activate the panel, just updates its content.
40494      */
40495     refresh : function(){
40496         if(this.refreshDelegate){
40497            this.loaded = false;
40498            this.refreshDelegate();
40499         }
40500     },
40501     
40502     /**
40503      * Destroys this panel
40504      */
40505     destroy : function(){
40506         this.el.removeAllListeners();
40507         var tempEl = document.createElement("span");
40508         tempEl.appendChild(this.el.dom);
40509         tempEl.innerHTML = "";
40510         this.el.remove();
40511         this.el = null;
40512     },
40513     
40514     /**
40515      * form - if the content panel contains a form - this is a reference to it.
40516      * @type {Roo.form.Form}
40517      */
40518     form : false,
40519     /**
40520      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40521      *    This contains a reference to it.
40522      * @type {Roo.View}
40523      */
40524     view : false,
40525     
40526       /**
40527      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40528      * <pre><code>
40529
40530 layout.addxtype({
40531        xtype : 'Form',
40532        items: [ .... ]
40533    }
40534 );
40535
40536 </code></pre>
40537      * @param {Object} cfg Xtype definition of item to add.
40538      */
40539     
40540     
40541     getChildContainer: function () {
40542         return this.getEl();
40543     }
40544     
40545     
40546     /*
40547         var  ret = new Roo.factory(cfg);
40548         return ret;
40549         
40550         
40551         // add form..
40552         if (cfg.xtype.match(/^Form$/)) {
40553             
40554             var el;
40555             //if (this.footer) {
40556             //    el = this.footer.container.insertSibling(false, 'before');
40557             //} else {
40558                 el = this.el.createChild();
40559             //}
40560
40561             this.form = new  Roo.form.Form(cfg);
40562             
40563             
40564             if ( this.form.allItems.length) {
40565                 this.form.render(el.dom);
40566             }
40567             return this.form;
40568         }
40569         // should only have one of theses..
40570         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40571             // views.. should not be just added - used named prop 'view''
40572             
40573             cfg.el = this.el.appendChild(document.createElement("div"));
40574             // factory?
40575             
40576             var ret = new Roo.factory(cfg);
40577              
40578              ret.render && ret.render(false, ''); // render blank..
40579             this.view = ret;
40580             return ret;
40581         }
40582         return false;
40583     }
40584     \*/
40585 });
40586  
40587 /**
40588  * @class Roo.bootstrap.panel.Grid
40589  * @extends Roo.bootstrap.panel.Content
40590  * @constructor
40591  * Create a new GridPanel.
40592  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40593  * @param {Object} config A the config object
40594   
40595  */
40596
40597
40598
40599 Roo.bootstrap.panel.Grid = function(config)
40600 {
40601     
40602       
40603     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40604         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40605
40606     config.el = this.wrapper;
40607     //this.el = this.wrapper;
40608     
40609       if (config.container) {
40610         // ctor'ed from a Border/panel.grid
40611         
40612         
40613         this.wrapper.setStyle("overflow", "hidden");
40614         this.wrapper.addClass('roo-grid-container');
40615
40616     }
40617     
40618     
40619     if(config.toolbar){
40620         var tool_el = this.wrapper.createChild();    
40621         this.toolbar = Roo.factory(config.toolbar);
40622         var ti = [];
40623         if (config.toolbar.items) {
40624             ti = config.toolbar.items ;
40625             delete config.toolbar.items ;
40626         }
40627         
40628         var nitems = [];
40629         this.toolbar.render(tool_el);
40630         for(var i =0;i < ti.length;i++) {
40631           //  Roo.log(['add child', items[i]]);
40632             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40633         }
40634         this.toolbar.items = nitems;
40635         
40636         delete config.toolbar;
40637     }
40638     
40639     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40640     config.grid.scrollBody = true;;
40641     config.grid.monitorWindowResize = false; // turn off autosizing
40642     config.grid.autoHeight = false;
40643     config.grid.autoWidth = false;
40644     
40645     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40646     
40647     if (config.background) {
40648         // render grid on panel activation (if panel background)
40649         this.on('activate', function(gp) {
40650             if (!gp.grid.rendered) {
40651                 gp.grid.render(this.wrapper);
40652                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40653             }
40654         });
40655             
40656     } else {
40657         this.grid.render(this.wrapper);
40658         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40659
40660     }
40661     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40662     // ??? needed ??? config.el = this.wrapper;
40663     
40664     
40665     
40666   
40667     // xtype created footer. - not sure if will work as we normally have to render first..
40668     if (this.footer && !this.footer.el && this.footer.xtype) {
40669         
40670         var ctr = this.grid.getView().getFooterPanel(true);
40671         this.footer.dataSource = this.grid.dataSource;
40672         this.footer = Roo.factory(this.footer, Roo);
40673         this.footer.render(ctr);
40674         
40675     }
40676     
40677     
40678     
40679     
40680      
40681 };
40682
40683 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40684     getId : function(){
40685         return this.grid.id;
40686     },
40687     
40688     /**
40689      * Returns the grid for this panel
40690      * @return {Roo.bootstrap.Table} 
40691      */
40692     getGrid : function(){
40693         return this.grid;    
40694     },
40695     
40696     setSize : function(width, height){
40697         if(!this.ignoreResize(width, height)){
40698             var grid = this.grid;
40699             var size = this.adjustForComponents(width, height);
40700             // tfoot is not a footer?
40701           
40702             
40703             var gridel = grid.getGridEl();
40704             gridel.setSize(size.width, size.height);
40705             
40706             var tbd = grid.getGridEl().select('tbody', true).first();
40707             var thd = grid.getGridEl().select('thead',true).first();
40708             var tbf= grid.getGridEl().select('tfoot', true).first();
40709
40710             if (tbf) {
40711                 size.height -= tbf.getHeight();
40712             }
40713             if (thd) {
40714                 size.height -= thd.getHeight();
40715             }
40716             
40717             tbd.setSize(size.width, size.height );
40718             // this is for the account management tab -seems to work there.
40719             var thd = grid.getGridEl().select('thead',true).first();
40720             //if (tbd) {
40721             //    tbd.setSize(size.width, size.height - thd.getHeight());
40722             //}
40723              
40724             grid.autoSize();
40725         }
40726     },
40727      
40728     
40729     
40730     beforeSlide : function(){
40731         this.grid.getView().scroller.clip();
40732     },
40733     
40734     afterSlide : function(){
40735         this.grid.getView().scroller.unclip();
40736     },
40737     
40738     destroy : function(){
40739         this.grid.destroy();
40740         delete this.grid;
40741         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40742     }
40743 });
40744
40745 /**
40746  * @class Roo.bootstrap.panel.Nest
40747  * @extends Roo.bootstrap.panel.Content
40748  * @constructor
40749  * Create a new Panel, that can contain a layout.Border.
40750  * 
40751  * 
40752  * @param {Roo.BorderLayout} layout The layout for this panel
40753  * @param {String/Object} config A string to set only the title or a config object
40754  */
40755 Roo.bootstrap.panel.Nest = function(config)
40756 {
40757     // construct with only one argument..
40758     /* FIXME - implement nicer consturctors
40759     if (layout.layout) {
40760         config = layout;
40761         layout = config.layout;
40762         delete config.layout;
40763     }
40764     if (layout.xtype && !layout.getEl) {
40765         // then layout needs constructing..
40766         layout = Roo.factory(layout, Roo);
40767     }
40768     */
40769     
40770     config.el =  config.layout.getEl();
40771     
40772     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40773     
40774     config.layout.monitorWindowResize = false; // turn off autosizing
40775     this.layout = config.layout;
40776     this.layout.getEl().addClass("roo-layout-nested-layout");
40777     this.layout.parent = this;
40778     
40779     
40780     
40781     
40782 };
40783
40784 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40785
40786     setSize : function(width, height){
40787         if(!this.ignoreResize(width, height)){
40788             var size = this.adjustForComponents(width, height);
40789             var el = this.layout.getEl();
40790             if (size.height < 1) {
40791                 el.setWidth(size.width);   
40792             } else {
40793                 el.setSize(size.width, size.height);
40794             }
40795             var touch = el.dom.offsetWidth;
40796             this.layout.layout();
40797             // ie requires a double layout on the first pass
40798             if(Roo.isIE && !this.initialized){
40799                 this.initialized = true;
40800                 this.layout.layout();
40801             }
40802         }
40803     },
40804     
40805     // activate all subpanels if not currently active..
40806     
40807     setActiveState : function(active){
40808         this.active = active;
40809         this.setActiveClass(active);
40810         
40811         if(!active){
40812             this.fireEvent("deactivate", this);
40813             return;
40814         }
40815         
40816         this.fireEvent("activate", this);
40817         // not sure if this should happen before or after..
40818         if (!this.layout) {
40819             return; // should not happen..
40820         }
40821         var reg = false;
40822         for (var r in this.layout.regions) {
40823             reg = this.layout.getRegion(r);
40824             if (reg.getActivePanel()) {
40825                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40826                 reg.setActivePanel(reg.getActivePanel());
40827                 continue;
40828             }
40829             if (!reg.panels.length) {
40830                 continue;
40831             }
40832             reg.showPanel(reg.getPanel(0));
40833         }
40834         
40835         
40836         
40837         
40838     },
40839     
40840     /**
40841      * Returns the nested BorderLayout for this panel
40842      * @return {Roo.BorderLayout} 
40843      */
40844     getLayout : function(){
40845         return this.layout;
40846     },
40847     
40848      /**
40849      * Adds a xtype elements to the layout of the nested panel
40850      * <pre><code>
40851
40852 panel.addxtype({
40853        xtype : 'ContentPanel',
40854        region: 'west',
40855        items: [ .... ]
40856    }
40857 );
40858
40859 panel.addxtype({
40860         xtype : 'NestedLayoutPanel',
40861         region: 'west',
40862         layout: {
40863            center: { },
40864            west: { }   
40865         },
40866         items : [ ... list of content panels or nested layout panels.. ]
40867    }
40868 );
40869 </code></pre>
40870      * @param {Object} cfg Xtype definition of item to add.
40871      */
40872     addxtype : function(cfg) {
40873         return this.layout.addxtype(cfg);
40874     
40875     }
40876 });/*
40877  * Based on:
40878  * Ext JS Library 1.1.1
40879  * Copyright(c) 2006-2007, Ext JS, LLC.
40880  *
40881  * Originally Released Under LGPL - original licence link has changed is not relivant.
40882  *
40883  * Fork - LGPL
40884  * <script type="text/javascript">
40885  */
40886 /**
40887  * @class Roo.TabPanel
40888  * @extends Roo.util.Observable
40889  * A lightweight tab container.
40890  * <br><br>
40891  * Usage:
40892  * <pre><code>
40893 // basic tabs 1, built from existing content
40894 var tabs = new Roo.TabPanel("tabs1");
40895 tabs.addTab("script", "View Script");
40896 tabs.addTab("markup", "View Markup");
40897 tabs.activate("script");
40898
40899 // more advanced tabs, built from javascript
40900 var jtabs = new Roo.TabPanel("jtabs");
40901 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40902
40903 // set up the UpdateManager
40904 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40905 var updater = tab2.getUpdateManager();
40906 updater.setDefaultUrl("ajax1.htm");
40907 tab2.on('activate', updater.refresh, updater, true);
40908
40909 // Use setUrl for Ajax loading
40910 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40911 tab3.setUrl("ajax2.htm", null, true);
40912
40913 // Disabled tab
40914 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40915 tab4.disable();
40916
40917 jtabs.activate("jtabs-1");
40918  * </code></pre>
40919  * @constructor
40920  * Create a new TabPanel.
40921  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40922  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40923  */
40924 Roo.bootstrap.panel.Tabs = function(config){
40925     /**
40926     * The container element for this TabPanel.
40927     * @type Roo.Element
40928     */
40929     this.el = Roo.get(config.el);
40930     delete config.el;
40931     if(config){
40932         if(typeof config == "boolean"){
40933             this.tabPosition = config ? "bottom" : "top";
40934         }else{
40935             Roo.apply(this, config);
40936         }
40937     }
40938     
40939     if(this.tabPosition == "bottom"){
40940         // if tabs are at the bottom = create the body first.
40941         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40942         this.el.addClass("roo-tabs-bottom");
40943     }
40944     // next create the tabs holders
40945     
40946     if (this.tabPosition == "west"){
40947         
40948         var reg = this.region; // fake it..
40949         while (reg) {
40950             if (!reg.mgr.parent) {
40951                 break;
40952             }
40953             reg = reg.mgr.parent.region;
40954         }
40955         Roo.log("got nest?");
40956         Roo.log(reg);
40957         if (reg.mgr.getRegion('west')) {
40958             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40959             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40960             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40961             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40962             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40963         
40964             
40965         }
40966         
40967         
40968     } else {
40969      
40970         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40971         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40972         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40973         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40974     }
40975     
40976     
40977     if(Roo.isIE){
40978         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40979     }
40980     
40981     // finally - if tabs are at the top, then create the body last..
40982     if(this.tabPosition != "bottom"){
40983         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40984          * @type Roo.Element
40985          */
40986         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40987         this.el.addClass("roo-tabs-top");
40988     }
40989     this.items = [];
40990
40991     this.bodyEl.setStyle("position", "relative");
40992
40993     this.active = null;
40994     this.activateDelegate = this.activate.createDelegate(this);
40995
40996     this.addEvents({
40997         /**
40998          * @event tabchange
40999          * Fires when the active tab changes
41000          * @param {Roo.TabPanel} this
41001          * @param {Roo.TabPanelItem} activePanel The new active tab
41002          */
41003         "tabchange": true,
41004         /**
41005          * @event beforetabchange
41006          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41007          * @param {Roo.TabPanel} this
41008          * @param {Object} e Set cancel to true on this object to cancel the tab change
41009          * @param {Roo.TabPanelItem} tab The tab being changed to
41010          */
41011         "beforetabchange" : true
41012     });
41013
41014     Roo.EventManager.onWindowResize(this.onResize, this);
41015     this.cpad = this.el.getPadding("lr");
41016     this.hiddenCount = 0;
41017
41018
41019     // toolbar on the tabbar support...
41020     if (this.toolbar) {
41021         alert("no toolbar support yet");
41022         this.toolbar  = false;
41023         /*
41024         var tcfg = this.toolbar;
41025         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41026         this.toolbar = new Roo.Toolbar(tcfg);
41027         if (Roo.isSafari) {
41028             var tbl = tcfg.container.child('table', true);
41029             tbl.setAttribute('width', '100%');
41030         }
41031         */
41032         
41033     }
41034    
41035
41036
41037     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41038 };
41039
41040 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41041     /*
41042      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41043      */
41044     tabPosition : "top",
41045     /*
41046      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41047      */
41048     currentTabWidth : 0,
41049     /*
41050      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41051      */
41052     minTabWidth : 40,
41053     /*
41054      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41055      */
41056     maxTabWidth : 250,
41057     /*
41058      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41059      */
41060     preferredTabWidth : 175,
41061     /*
41062      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41063      */
41064     resizeTabs : false,
41065     /*
41066      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41067      */
41068     monitorResize : true,
41069     /*
41070      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41071      */
41072     toolbar : false,  // set by caller..
41073     
41074     region : false, /// set by caller
41075     
41076     disableTooltips : true, // not used yet...
41077
41078     /**
41079      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41080      * @param {String} id The id of the div to use <b>or create</b>
41081      * @param {String} text The text for the tab
41082      * @param {String} content (optional) Content to put in the TabPanelItem body
41083      * @param {Boolean} closable (optional) True to create a close icon on the tab
41084      * @return {Roo.TabPanelItem} The created TabPanelItem
41085      */
41086     addTab : function(id, text, content, closable, tpl)
41087     {
41088         var item = new Roo.bootstrap.panel.TabItem({
41089             panel: this,
41090             id : id,
41091             text : text,
41092             closable : closable,
41093             tpl : tpl
41094         });
41095         this.addTabItem(item);
41096         if(content){
41097             item.setContent(content);
41098         }
41099         return item;
41100     },
41101
41102     /**
41103      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41104      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41105      * @return {Roo.TabPanelItem}
41106      */
41107     getTab : function(id){
41108         return this.items[id];
41109     },
41110
41111     /**
41112      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41113      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41114      */
41115     hideTab : function(id){
41116         var t = this.items[id];
41117         if(!t.isHidden()){
41118            t.setHidden(true);
41119            this.hiddenCount++;
41120            this.autoSizeTabs();
41121         }
41122     },
41123
41124     /**
41125      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41126      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41127      */
41128     unhideTab : function(id){
41129         var t = this.items[id];
41130         if(t.isHidden()){
41131            t.setHidden(false);
41132            this.hiddenCount--;
41133            this.autoSizeTabs();
41134         }
41135     },
41136
41137     /**
41138      * Adds an existing {@link Roo.TabPanelItem}.
41139      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41140      */
41141     addTabItem : function(item)
41142     {
41143         this.items[item.id] = item;
41144         this.items.push(item);
41145         this.autoSizeTabs();
41146       //  if(this.resizeTabs){
41147     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41148   //         this.autoSizeTabs();
41149 //        }else{
41150 //            item.autoSize();
41151        // }
41152     },
41153
41154     /**
41155      * Removes a {@link Roo.TabPanelItem}.
41156      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41157      */
41158     removeTab : function(id){
41159         var items = this.items;
41160         var tab = items[id];
41161         if(!tab) { return; }
41162         var index = items.indexOf(tab);
41163         if(this.active == tab && items.length > 1){
41164             var newTab = this.getNextAvailable(index);
41165             if(newTab) {
41166                 newTab.activate();
41167             }
41168         }
41169         this.stripEl.dom.removeChild(tab.pnode.dom);
41170         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41171             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41172         }
41173         items.splice(index, 1);
41174         delete this.items[tab.id];
41175         tab.fireEvent("close", tab);
41176         tab.purgeListeners();
41177         this.autoSizeTabs();
41178     },
41179
41180     getNextAvailable : function(start){
41181         var items = this.items;
41182         var index = start;
41183         // look for a next tab that will slide over to
41184         // replace the one being removed
41185         while(index < items.length){
41186             var item = items[++index];
41187             if(item && !item.isHidden()){
41188                 return item;
41189             }
41190         }
41191         // if one isn't found select the previous tab (on the left)
41192         index = start;
41193         while(index >= 0){
41194             var item = items[--index];
41195             if(item && !item.isHidden()){
41196                 return item;
41197             }
41198         }
41199         return null;
41200     },
41201
41202     /**
41203      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41204      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41205      */
41206     disableTab : function(id){
41207         var tab = this.items[id];
41208         if(tab && this.active != tab){
41209             tab.disable();
41210         }
41211     },
41212
41213     /**
41214      * Enables a {@link Roo.TabPanelItem} that is disabled.
41215      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41216      */
41217     enableTab : function(id){
41218         var tab = this.items[id];
41219         tab.enable();
41220     },
41221
41222     /**
41223      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41224      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41225      * @return {Roo.TabPanelItem} The TabPanelItem.
41226      */
41227     activate : function(id)
41228     {
41229         //Roo.log('activite:'  + id);
41230         
41231         var tab = this.items[id];
41232         if(!tab){
41233             return null;
41234         }
41235         if(tab == this.active || tab.disabled){
41236             return tab;
41237         }
41238         var e = {};
41239         this.fireEvent("beforetabchange", this, e, tab);
41240         if(e.cancel !== true && !tab.disabled){
41241             if(this.active){
41242                 this.active.hide();
41243             }
41244             this.active = this.items[id];
41245             this.active.show();
41246             this.fireEvent("tabchange", this, this.active);
41247         }
41248         return tab;
41249     },
41250
41251     /**
41252      * Gets the active {@link Roo.TabPanelItem}.
41253      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41254      */
41255     getActiveTab : function(){
41256         return this.active;
41257     },
41258
41259     /**
41260      * Updates the tab body element to fit the height of the container element
41261      * for overflow scrolling
41262      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41263      */
41264     syncHeight : function(targetHeight){
41265         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41266         var bm = this.bodyEl.getMargins();
41267         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41268         this.bodyEl.setHeight(newHeight);
41269         return newHeight;
41270     },
41271
41272     onResize : function(){
41273         if(this.monitorResize){
41274             this.autoSizeTabs();
41275         }
41276     },
41277
41278     /**
41279      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41280      */
41281     beginUpdate : function(){
41282         this.updating = true;
41283     },
41284
41285     /**
41286      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41287      */
41288     endUpdate : function(){
41289         this.updating = false;
41290         this.autoSizeTabs();
41291     },
41292
41293     /**
41294      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41295      */
41296     autoSizeTabs : function()
41297     {
41298         var count = this.items.length;
41299         var vcount = count - this.hiddenCount;
41300         
41301         if (vcount < 2) {
41302             this.stripEl.hide();
41303         } else {
41304             this.stripEl.show();
41305         }
41306         
41307         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41308             return;
41309         }
41310         
41311         
41312         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41313         var availWidth = Math.floor(w / vcount);
41314         var b = this.stripBody;
41315         if(b.getWidth() > w){
41316             var tabs = this.items;
41317             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41318             if(availWidth < this.minTabWidth){
41319                 /*if(!this.sleft){    // incomplete scrolling code
41320                     this.createScrollButtons();
41321                 }
41322                 this.showScroll();
41323                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41324             }
41325         }else{
41326             if(this.currentTabWidth < this.preferredTabWidth){
41327                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41328             }
41329         }
41330     },
41331
41332     /**
41333      * Returns the number of tabs in this TabPanel.
41334      * @return {Number}
41335      */
41336      getCount : function(){
41337          return this.items.length;
41338      },
41339
41340     /**
41341      * Resizes all the tabs to the passed width
41342      * @param {Number} The new width
41343      */
41344     setTabWidth : function(width){
41345         this.currentTabWidth = width;
41346         for(var i = 0, len = this.items.length; i < len; i++) {
41347                 if(!this.items[i].isHidden()) {
41348                 this.items[i].setWidth(width);
41349             }
41350         }
41351     },
41352
41353     /**
41354      * Destroys this TabPanel
41355      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41356      */
41357     destroy : function(removeEl){
41358         Roo.EventManager.removeResizeListener(this.onResize, this);
41359         for(var i = 0, len = this.items.length; i < len; i++){
41360             this.items[i].purgeListeners();
41361         }
41362         if(removeEl === true){
41363             this.el.update("");
41364             this.el.remove();
41365         }
41366     },
41367     
41368     createStrip : function(container)
41369     {
41370         var strip = document.createElement("nav");
41371         strip.className = Roo.bootstrap.version == 4 ?
41372             "navbar-light bg-light" : 
41373             "navbar navbar-default"; //"x-tabs-wrap";
41374         container.appendChild(strip);
41375         return strip;
41376     },
41377     
41378     createStripList : function(strip)
41379     {
41380         // div wrapper for retard IE
41381         // returns the "tr" element.
41382         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41383         //'<div class="x-tabs-strip-wrap">'+
41384           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41385           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41386         return strip.firstChild; //.firstChild.firstChild.firstChild;
41387     },
41388     createBody : function(container)
41389     {
41390         var body = document.createElement("div");
41391         Roo.id(body, "tab-body");
41392         //Roo.fly(body).addClass("x-tabs-body");
41393         Roo.fly(body).addClass("tab-content");
41394         container.appendChild(body);
41395         return body;
41396     },
41397     createItemBody :function(bodyEl, id){
41398         var body = Roo.getDom(id);
41399         if(!body){
41400             body = document.createElement("div");
41401             body.id = id;
41402         }
41403         //Roo.fly(body).addClass("x-tabs-item-body");
41404         Roo.fly(body).addClass("tab-pane");
41405          bodyEl.insertBefore(body, bodyEl.firstChild);
41406         return body;
41407     },
41408     /** @private */
41409     createStripElements :  function(stripEl, text, closable, tpl)
41410     {
41411         var td = document.createElement("li"); // was td..
41412         td.className = 'nav-item';
41413         
41414         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41415         
41416         
41417         stripEl.appendChild(td);
41418         /*if(closable){
41419             td.className = "x-tabs-closable";
41420             if(!this.closeTpl){
41421                 this.closeTpl = new Roo.Template(
41422                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41423                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41424                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41425                 );
41426             }
41427             var el = this.closeTpl.overwrite(td, {"text": text});
41428             var close = el.getElementsByTagName("div")[0];
41429             var inner = el.getElementsByTagName("em")[0];
41430             return {"el": el, "close": close, "inner": inner};
41431         } else {
41432         */
41433         // not sure what this is..
41434 //            if(!this.tabTpl){
41435                 //this.tabTpl = new Roo.Template(
41436                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41437                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41438                 //);
41439 //                this.tabTpl = new Roo.Template(
41440 //                   '<a href="#">' +
41441 //                   '<span unselectable="on"' +
41442 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41443 //                            ' >{text}</span></a>'
41444 //                );
41445 //                
41446 //            }
41447
41448
41449             var template = tpl || this.tabTpl || false;
41450             
41451             if(!template){
41452                 template =  new Roo.Template(
41453                         Roo.bootstrap.version == 4 ? 
41454                             (
41455                                 '<a class="nav-link" href="#" unselectable="on"' +
41456                                      (this.disableTooltips ? '' : ' title="{text}"') +
41457                                      ' >{text}</a>'
41458                             ) : (
41459                                 '<a class="nav-link" href="#">' +
41460                                 '<span unselectable="on"' +
41461                                          (this.disableTooltips ? '' : ' title="{text}"') +
41462                                     ' >{text}</span></a>'
41463                             )
41464                 );
41465             }
41466             
41467             switch (typeof(template)) {
41468                 case 'object' :
41469                     break;
41470                 case 'string' :
41471                     template = new Roo.Template(template);
41472                     break;
41473                 default :
41474                     break;
41475             }
41476             
41477             var el = template.overwrite(td, {"text": text});
41478             
41479             var inner = el.getElementsByTagName("span")[0];
41480             
41481             return {"el": el, "inner": inner};
41482             
41483     }
41484         
41485     
41486 });
41487
41488 /**
41489  * @class Roo.TabPanelItem
41490  * @extends Roo.util.Observable
41491  * Represents an individual item (tab plus body) in a TabPanel.
41492  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41493  * @param {String} id The id of this TabPanelItem
41494  * @param {String} text The text for the tab of this TabPanelItem
41495  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41496  */
41497 Roo.bootstrap.panel.TabItem = function(config){
41498     /**
41499      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41500      * @type Roo.TabPanel
41501      */
41502     this.tabPanel = config.panel;
41503     /**
41504      * The id for this TabPanelItem
41505      * @type String
41506      */
41507     this.id = config.id;
41508     /** @private */
41509     this.disabled = false;
41510     /** @private */
41511     this.text = config.text;
41512     /** @private */
41513     this.loaded = false;
41514     this.closable = config.closable;
41515
41516     /**
41517      * The body element for this TabPanelItem.
41518      * @type Roo.Element
41519      */
41520     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41521     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41522     this.bodyEl.setStyle("display", "block");
41523     this.bodyEl.setStyle("zoom", "1");
41524     //this.hideAction();
41525
41526     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41527     /** @private */
41528     this.el = Roo.get(els.el);
41529     this.inner = Roo.get(els.inner, true);
41530      this.textEl = Roo.bootstrap.version == 4 ?
41531         this.el : Roo.get(this.el.dom.firstChild, true);
41532
41533     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41534     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41535
41536     
41537 //    this.el.on("mousedown", this.onTabMouseDown, this);
41538     this.el.on("click", this.onTabClick, this);
41539     /** @private */
41540     if(config.closable){
41541         var c = Roo.get(els.close, true);
41542         c.dom.title = this.closeText;
41543         c.addClassOnOver("close-over");
41544         c.on("click", this.closeClick, this);
41545      }
41546
41547     this.addEvents({
41548          /**
41549          * @event activate
41550          * Fires when this tab becomes the active tab.
41551          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41552          * @param {Roo.TabPanelItem} this
41553          */
41554         "activate": true,
41555         /**
41556          * @event beforeclose
41557          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41558          * @param {Roo.TabPanelItem} this
41559          * @param {Object} e Set cancel to true on this object to cancel the close.
41560          */
41561         "beforeclose": true,
41562         /**
41563          * @event close
41564          * Fires when this tab is closed.
41565          * @param {Roo.TabPanelItem} this
41566          */
41567          "close": true,
41568         /**
41569          * @event deactivate
41570          * Fires when this tab is no longer the active tab.
41571          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41572          * @param {Roo.TabPanelItem} this
41573          */
41574          "deactivate" : true
41575     });
41576     this.hidden = false;
41577
41578     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41579 };
41580
41581 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41582            {
41583     purgeListeners : function(){
41584        Roo.util.Observable.prototype.purgeListeners.call(this);
41585        this.el.removeAllListeners();
41586     },
41587     /**
41588      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41589      */
41590     show : function(){
41591         this.status_node.addClass("active");
41592         this.showAction();
41593         if(Roo.isOpera){
41594             this.tabPanel.stripWrap.repaint();
41595         }
41596         this.fireEvent("activate", this.tabPanel, this);
41597     },
41598
41599     /**
41600      * Returns true if this tab is the active tab.
41601      * @return {Boolean}
41602      */
41603     isActive : function(){
41604         return this.tabPanel.getActiveTab() == this;
41605     },
41606
41607     /**
41608      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41609      */
41610     hide : function(){
41611         this.status_node.removeClass("active");
41612         this.hideAction();
41613         this.fireEvent("deactivate", this.tabPanel, this);
41614     },
41615
41616     hideAction : function(){
41617         this.bodyEl.hide();
41618         this.bodyEl.setStyle("position", "absolute");
41619         this.bodyEl.setLeft("-20000px");
41620         this.bodyEl.setTop("-20000px");
41621     },
41622
41623     showAction : function(){
41624         this.bodyEl.setStyle("position", "relative");
41625         this.bodyEl.setTop("");
41626         this.bodyEl.setLeft("");
41627         this.bodyEl.show();
41628     },
41629
41630     /**
41631      * Set the tooltip for the tab.
41632      * @param {String} tooltip The tab's tooltip
41633      */
41634     setTooltip : function(text){
41635         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41636             this.textEl.dom.qtip = text;
41637             this.textEl.dom.removeAttribute('title');
41638         }else{
41639             this.textEl.dom.title = text;
41640         }
41641     },
41642
41643     onTabClick : function(e){
41644         e.preventDefault();
41645         this.tabPanel.activate(this.id);
41646     },
41647
41648     onTabMouseDown : function(e){
41649         e.preventDefault();
41650         this.tabPanel.activate(this.id);
41651     },
41652 /*
41653     getWidth : function(){
41654         return this.inner.getWidth();
41655     },
41656
41657     setWidth : function(width){
41658         var iwidth = width - this.linode.getPadding("lr");
41659         this.inner.setWidth(iwidth);
41660         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41661         this.linode.setWidth(width);
41662     },
41663 */
41664     /**
41665      * Show or hide the tab
41666      * @param {Boolean} hidden True to hide or false to show.
41667      */
41668     setHidden : function(hidden){
41669         this.hidden = hidden;
41670         this.linode.setStyle("display", hidden ? "none" : "");
41671     },
41672
41673     /**
41674      * Returns true if this tab is "hidden"
41675      * @return {Boolean}
41676      */
41677     isHidden : function(){
41678         return this.hidden;
41679     },
41680
41681     /**
41682      * Returns the text for this tab
41683      * @return {String}
41684      */
41685     getText : function(){
41686         return this.text;
41687     },
41688     /*
41689     autoSize : function(){
41690         //this.el.beginMeasure();
41691         this.textEl.setWidth(1);
41692         /*
41693          *  #2804 [new] Tabs in Roojs
41694          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41695          */
41696         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41697         //this.el.endMeasure();
41698     //},
41699
41700     /**
41701      * Sets the text for the tab (Note: this also sets the tooltip text)
41702      * @param {String} text The tab's text and tooltip
41703      */
41704     setText : function(text){
41705         this.text = text;
41706         this.textEl.update(text);
41707         this.setTooltip(text);
41708         //if(!this.tabPanel.resizeTabs){
41709         //    this.autoSize();
41710         //}
41711     },
41712     /**
41713      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41714      */
41715     activate : function(){
41716         this.tabPanel.activate(this.id);
41717     },
41718
41719     /**
41720      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41721      */
41722     disable : function(){
41723         if(this.tabPanel.active != this){
41724             this.disabled = true;
41725             this.status_node.addClass("disabled");
41726         }
41727     },
41728
41729     /**
41730      * Enables this TabPanelItem if it was previously disabled.
41731      */
41732     enable : function(){
41733         this.disabled = false;
41734         this.status_node.removeClass("disabled");
41735     },
41736
41737     /**
41738      * Sets the content for this TabPanelItem.
41739      * @param {String} content The content
41740      * @param {Boolean} loadScripts true to look for and load scripts
41741      */
41742     setContent : function(content, loadScripts){
41743         this.bodyEl.update(content, loadScripts);
41744     },
41745
41746     /**
41747      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41748      * @return {Roo.UpdateManager} The UpdateManager
41749      */
41750     getUpdateManager : function(){
41751         return this.bodyEl.getUpdateManager();
41752     },
41753
41754     /**
41755      * Set a URL to be used to load the content for this TabPanelItem.
41756      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41757      * @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)
41758      * @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)
41759      * @return {Roo.UpdateManager} The UpdateManager
41760      */
41761     setUrl : function(url, params, loadOnce){
41762         if(this.refreshDelegate){
41763             this.un('activate', this.refreshDelegate);
41764         }
41765         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41766         this.on("activate", this.refreshDelegate);
41767         return this.bodyEl.getUpdateManager();
41768     },
41769
41770     /** @private */
41771     _handleRefresh : function(url, params, loadOnce){
41772         if(!loadOnce || !this.loaded){
41773             var updater = this.bodyEl.getUpdateManager();
41774             updater.update(url, params, this._setLoaded.createDelegate(this));
41775         }
41776     },
41777
41778     /**
41779      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41780      *   Will fail silently if the setUrl method has not been called.
41781      *   This does not activate the panel, just updates its content.
41782      */
41783     refresh : function(){
41784         if(this.refreshDelegate){
41785            this.loaded = false;
41786            this.refreshDelegate();
41787         }
41788     },
41789
41790     /** @private */
41791     _setLoaded : function(){
41792         this.loaded = true;
41793     },
41794
41795     /** @private */
41796     closeClick : function(e){
41797         var o = {};
41798         e.stopEvent();
41799         this.fireEvent("beforeclose", this, o);
41800         if(o.cancel !== true){
41801             this.tabPanel.removeTab(this.id);
41802         }
41803     },
41804     /**
41805      * The text displayed in the tooltip for the close icon.
41806      * @type String
41807      */
41808     closeText : "Close this tab"
41809 });
41810 /**
41811 *    This script refer to:
41812 *    Title: International Telephone Input
41813 *    Author: Jack O'Connor
41814 *    Code version:  v12.1.12
41815 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41816 **/
41817
41818 Roo.bootstrap.PhoneInputData = function() {
41819     var d = [
41820       [
41821         "Afghanistan (‫افغانستان‬‎)",
41822         "af",
41823         "93"
41824       ],
41825       [
41826         "Albania (Shqipëri)",
41827         "al",
41828         "355"
41829       ],
41830       [
41831         "Algeria (‫الجزائر‬‎)",
41832         "dz",
41833         "213"
41834       ],
41835       [
41836         "American Samoa",
41837         "as",
41838         "1684"
41839       ],
41840       [
41841         "Andorra",
41842         "ad",
41843         "376"
41844       ],
41845       [
41846         "Angola",
41847         "ao",
41848         "244"
41849       ],
41850       [
41851         "Anguilla",
41852         "ai",
41853         "1264"
41854       ],
41855       [
41856         "Antigua and Barbuda",
41857         "ag",
41858         "1268"
41859       ],
41860       [
41861         "Argentina",
41862         "ar",
41863         "54"
41864       ],
41865       [
41866         "Armenia (Հայաստան)",
41867         "am",
41868         "374"
41869       ],
41870       [
41871         "Aruba",
41872         "aw",
41873         "297"
41874       ],
41875       [
41876         "Australia",
41877         "au",
41878         "61",
41879         0
41880       ],
41881       [
41882         "Austria (Österreich)",
41883         "at",
41884         "43"
41885       ],
41886       [
41887         "Azerbaijan (Azərbaycan)",
41888         "az",
41889         "994"
41890       ],
41891       [
41892         "Bahamas",
41893         "bs",
41894         "1242"
41895       ],
41896       [
41897         "Bahrain (‫البحرين‬‎)",
41898         "bh",
41899         "973"
41900       ],
41901       [
41902         "Bangladesh (বাংলাদেশ)",
41903         "bd",
41904         "880"
41905       ],
41906       [
41907         "Barbados",
41908         "bb",
41909         "1246"
41910       ],
41911       [
41912         "Belarus (Беларусь)",
41913         "by",
41914         "375"
41915       ],
41916       [
41917         "Belgium (België)",
41918         "be",
41919         "32"
41920       ],
41921       [
41922         "Belize",
41923         "bz",
41924         "501"
41925       ],
41926       [
41927         "Benin (Bénin)",
41928         "bj",
41929         "229"
41930       ],
41931       [
41932         "Bermuda",
41933         "bm",
41934         "1441"
41935       ],
41936       [
41937         "Bhutan (འབྲུག)",
41938         "bt",
41939         "975"
41940       ],
41941       [
41942         "Bolivia",
41943         "bo",
41944         "591"
41945       ],
41946       [
41947         "Bosnia and Herzegovina (Босна и Херцеговина)",
41948         "ba",
41949         "387"
41950       ],
41951       [
41952         "Botswana",
41953         "bw",
41954         "267"
41955       ],
41956       [
41957         "Brazil (Brasil)",
41958         "br",
41959         "55"
41960       ],
41961       [
41962         "British Indian Ocean Territory",
41963         "io",
41964         "246"
41965       ],
41966       [
41967         "British Virgin Islands",
41968         "vg",
41969         "1284"
41970       ],
41971       [
41972         "Brunei",
41973         "bn",
41974         "673"
41975       ],
41976       [
41977         "Bulgaria (България)",
41978         "bg",
41979         "359"
41980       ],
41981       [
41982         "Burkina Faso",
41983         "bf",
41984         "226"
41985       ],
41986       [
41987         "Burundi (Uburundi)",
41988         "bi",
41989         "257"
41990       ],
41991       [
41992         "Cambodia (កម្ពុជា)",
41993         "kh",
41994         "855"
41995       ],
41996       [
41997         "Cameroon (Cameroun)",
41998         "cm",
41999         "237"
42000       ],
42001       [
42002         "Canada",
42003         "ca",
42004         "1",
42005         1,
42006         ["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"]
42007       ],
42008       [
42009         "Cape Verde (Kabu Verdi)",
42010         "cv",
42011         "238"
42012       ],
42013       [
42014         "Caribbean Netherlands",
42015         "bq",
42016         "599",
42017         1
42018       ],
42019       [
42020         "Cayman Islands",
42021         "ky",
42022         "1345"
42023       ],
42024       [
42025         "Central African Republic (République centrafricaine)",
42026         "cf",
42027         "236"
42028       ],
42029       [
42030         "Chad (Tchad)",
42031         "td",
42032         "235"
42033       ],
42034       [
42035         "Chile",
42036         "cl",
42037         "56"
42038       ],
42039       [
42040         "China (中国)",
42041         "cn",
42042         "86"
42043       ],
42044       [
42045         "Christmas Island",
42046         "cx",
42047         "61",
42048         2
42049       ],
42050       [
42051         "Cocos (Keeling) Islands",
42052         "cc",
42053         "61",
42054         1
42055       ],
42056       [
42057         "Colombia",
42058         "co",
42059         "57"
42060       ],
42061       [
42062         "Comoros (‫جزر القمر‬‎)",
42063         "km",
42064         "269"
42065       ],
42066       [
42067         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42068         "cd",
42069         "243"
42070       ],
42071       [
42072         "Congo (Republic) (Congo-Brazzaville)",
42073         "cg",
42074         "242"
42075       ],
42076       [
42077         "Cook Islands",
42078         "ck",
42079         "682"
42080       ],
42081       [
42082         "Costa Rica",
42083         "cr",
42084         "506"
42085       ],
42086       [
42087         "Côte d’Ivoire",
42088         "ci",
42089         "225"
42090       ],
42091       [
42092         "Croatia (Hrvatska)",
42093         "hr",
42094         "385"
42095       ],
42096       [
42097         "Cuba",
42098         "cu",
42099         "53"
42100       ],
42101       [
42102         "Curaçao",
42103         "cw",
42104         "599",
42105         0
42106       ],
42107       [
42108         "Cyprus (Κύπρος)",
42109         "cy",
42110         "357"
42111       ],
42112       [
42113         "Czech Republic (Česká republika)",
42114         "cz",
42115         "420"
42116       ],
42117       [
42118         "Denmark (Danmark)",
42119         "dk",
42120         "45"
42121       ],
42122       [
42123         "Djibouti",
42124         "dj",
42125         "253"
42126       ],
42127       [
42128         "Dominica",
42129         "dm",
42130         "1767"
42131       ],
42132       [
42133         "Dominican Republic (República Dominicana)",
42134         "do",
42135         "1",
42136         2,
42137         ["809", "829", "849"]
42138       ],
42139       [
42140         "Ecuador",
42141         "ec",
42142         "593"
42143       ],
42144       [
42145         "Egypt (‫مصر‬‎)",
42146         "eg",
42147         "20"
42148       ],
42149       [
42150         "El Salvador",
42151         "sv",
42152         "503"
42153       ],
42154       [
42155         "Equatorial Guinea (Guinea Ecuatorial)",
42156         "gq",
42157         "240"
42158       ],
42159       [
42160         "Eritrea",
42161         "er",
42162         "291"
42163       ],
42164       [
42165         "Estonia (Eesti)",
42166         "ee",
42167         "372"
42168       ],
42169       [
42170         "Ethiopia",
42171         "et",
42172         "251"
42173       ],
42174       [
42175         "Falkland Islands (Islas Malvinas)",
42176         "fk",
42177         "500"
42178       ],
42179       [
42180         "Faroe Islands (Føroyar)",
42181         "fo",
42182         "298"
42183       ],
42184       [
42185         "Fiji",
42186         "fj",
42187         "679"
42188       ],
42189       [
42190         "Finland (Suomi)",
42191         "fi",
42192         "358",
42193         0
42194       ],
42195       [
42196         "France",
42197         "fr",
42198         "33"
42199       ],
42200       [
42201         "French Guiana (Guyane française)",
42202         "gf",
42203         "594"
42204       ],
42205       [
42206         "French Polynesia (Polynésie française)",
42207         "pf",
42208         "689"
42209       ],
42210       [
42211         "Gabon",
42212         "ga",
42213         "241"
42214       ],
42215       [
42216         "Gambia",
42217         "gm",
42218         "220"
42219       ],
42220       [
42221         "Georgia (საქართველო)",
42222         "ge",
42223         "995"
42224       ],
42225       [
42226         "Germany (Deutschland)",
42227         "de",
42228         "49"
42229       ],
42230       [
42231         "Ghana (Gaana)",
42232         "gh",
42233         "233"
42234       ],
42235       [
42236         "Gibraltar",
42237         "gi",
42238         "350"
42239       ],
42240       [
42241         "Greece (Ελλάδα)",
42242         "gr",
42243         "30"
42244       ],
42245       [
42246         "Greenland (Kalaallit Nunaat)",
42247         "gl",
42248         "299"
42249       ],
42250       [
42251         "Grenada",
42252         "gd",
42253         "1473"
42254       ],
42255       [
42256         "Guadeloupe",
42257         "gp",
42258         "590",
42259         0
42260       ],
42261       [
42262         "Guam",
42263         "gu",
42264         "1671"
42265       ],
42266       [
42267         "Guatemala",
42268         "gt",
42269         "502"
42270       ],
42271       [
42272         "Guernsey",
42273         "gg",
42274         "44",
42275         1
42276       ],
42277       [
42278         "Guinea (Guinée)",
42279         "gn",
42280         "224"
42281       ],
42282       [
42283         "Guinea-Bissau (Guiné Bissau)",
42284         "gw",
42285         "245"
42286       ],
42287       [
42288         "Guyana",
42289         "gy",
42290         "592"
42291       ],
42292       [
42293         "Haiti",
42294         "ht",
42295         "509"
42296       ],
42297       [
42298         "Honduras",
42299         "hn",
42300         "504"
42301       ],
42302       [
42303         "Hong Kong (香港)",
42304         "hk",
42305         "852"
42306       ],
42307       [
42308         "Hungary (Magyarország)",
42309         "hu",
42310         "36"
42311       ],
42312       [
42313         "Iceland (Ísland)",
42314         "is",
42315         "354"
42316       ],
42317       [
42318         "India (भारत)",
42319         "in",
42320         "91"
42321       ],
42322       [
42323         "Indonesia",
42324         "id",
42325         "62"
42326       ],
42327       [
42328         "Iran (‫ایران‬‎)",
42329         "ir",
42330         "98"
42331       ],
42332       [
42333         "Iraq (‫العراق‬‎)",
42334         "iq",
42335         "964"
42336       ],
42337       [
42338         "Ireland",
42339         "ie",
42340         "353"
42341       ],
42342       [
42343         "Isle of Man",
42344         "im",
42345         "44",
42346         2
42347       ],
42348       [
42349         "Israel (‫ישראל‬‎)",
42350         "il",
42351         "972"
42352       ],
42353       [
42354         "Italy (Italia)",
42355         "it",
42356         "39",
42357         0
42358       ],
42359       [
42360         "Jamaica",
42361         "jm",
42362         "1876"
42363       ],
42364       [
42365         "Japan (日本)",
42366         "jp",
42367         "81"
42368       ],
42369       [
42370         "Jersey",
42371         "je",
42372         "44",
42373         3
42374       ],
42375       [
42376         "Jordan (‫الأردن‬‎)",
42377         "jo",
42378         "962"
42379       ],
42380       [
42381         "Kazakhstan (Казахстан)",
42382         "kz",
42383         "7",
42384         1
42385       ],
42386       [
42387         "Kenya",
42388         "ke",
42389         "254"
42390       ],
42391       [
42392         "Kiribati",
42393         "ki",
42394         "686"
42395       ],
42396       [
42397         "Kosovo",
42398         "xk",
42399         "383"
42400       ],
42401       [
42402         "Kuwait (‫الكويت‬‎)",
42403         "kw",
42404         "965"
42405       ],
42406       [
42407         "Kyrgyzstan (Кыргызстан)",
42408         "kg",
42409         "996"
42410       ],
42411       [
42412         "Laos (ລາວ)",
42413         "la",
42414         "856"
42415       ],
42416       [
42417         "Latvia (Latvija)",
42418         "lv",
42419         "371"
42420       ],
42421       [
42422         "Lebanon (‫لبنان‬‎)",
42423         "lb",
42424         "961"
42425       ],
42426       [
42427         "Lesotho",
42428         "ls",
42429         "266"
42430       ],
42431       [
42432         "Liberia",
42433         "lr",
42434         "231"
42435       ],
42436       [
42437         "Libya (‫ليبيا‬‎)",
42438         "ly",
42439         "218"
42440       ],
42441       [
42442         "Liechtenstein",
42443         "li",
42444         "423"
42445       ],
42446       [
42447         "Lithuania (Lietuva)",
42448         "lt",
42449         "370"
42450       ],
42451       [
42452         "Luxembourg",
42453         "lu",
42454         "352"
42455       ],
42456       [
42457         "Macau (澳門)",
42458         "mo",
42459         "853"
42460       ],
42461       [
42462         "Macedonia (FYROM) (Македонија)",
42463         "mk",
42464         "389"
42465       ],
42466       [
42467         "Madagascar (Madagasikara)",
42468         "mg",
42469         "261"
42470       ],
42471       [
42472         "Malawi",
42473         "mw",
42474         "265"
42475       ],
42476       [
42477         "Malaysia",
42478         "my",
42479         "60"
42480       ],
42481       [
42482         "Maldives",
42483         "mv",
42484         "960"
42485       ],
42486       [
42487         "Mali",
42488         "ml",
42489         "223"
42490       ],
42491       [
42492         "Malta",
42493         "mt",
42494         "356"
42495       ],
42496       [
42497         "Marshall Islands",
42498         "mh",
42499         "692"
42500       ],
42501       [
42502         "Martinique",
42503         "mq",
42504         "596"
42505       ],
42506       [
42507         "Mauritania (‫موريتانيا‬‎)",
42508         "mr",
42509         "222"
42510       ],
42511       [
42512         "Mauritius (Moris)",
42513         "mu",
42514         "230"
42515       ],
42516       [
42517         "Mayotte",
42518         "yt",
42519         "262",
42520         1
42521       ],
42522       [
42523         "Mexico (México)",
42524         "mx",
42525         "52"
42526       ],
42527       [
42528         "Micronesia",
42529         "fm",
42530         "691"
42531       ],
42532       [
42533         "Moldova (Republica Moldova)",
42534         "md",
42535         "373"
42536       ],
42537       [
42538         "Monaco",
42539         "mc",
42540         "377"
42541       ],
42542       [
42543         "Mongolia (Монгол)",
42544         "mn",
42545         "976"
42546       ],
42547       [
42548         "Montenegro (Crna Gora)",
42549         "me",
42550         "382"
42551       ],
42552       [
42553         "Montserrat",
42554         "ms",
42555         "1664"
42556       ],
42557       [
42558         "Morocco (‫المغرب‬‎)",
42559         "ma",
42560         "212",
42561         0
42562       ],
42563       [
42564         "Mozambique (Moçambique)",
42565         "mz",
42566         "258"
42567       ],
42568       [
42569         "Myanmar (Burma) (မြန်မာ)",
42570         "mm",
42571         "95"
42572       ],
42573       [
42574         "Namibia (Namibië)",
42575         "na",
42576         "264"
42577       ],
42578       [
42579         "Nauru",
42580         "nr",
42581         "674"
42582       ],
42583       [
42584         "Nepal (नेपाल)",
42585         "np",
42586         "977"
42587       ],
42588       [
42589         "Netherlands (Nederland)",
42590         "nl",
42591         "31"
42592       ],
42593       [
42594         "New Caledonia (Nouvelle-Calédonie)",
42595         "nc",
42596         "687"
42597       ],
42598       [
42599         "New Zealand",
42600         "nz",
42601         "64"
42602       ],
42603       [
42604         "Nicaragua",
42605         "ni",
42606         "505"
42607       ],
42608       [
42609         "Niger (Nijar)",
42610         "ne",
42611         "227"
42612       ],
42613       [
42614         "Nigeria",
42615         "ng",
42616         "234"
42617       ],
42618       [
42619         "Niue",
42620         "nu",
42621         "683"
42622       ],
42623       [
42624         "Norfolk Island",
42625         "nf",
42626         "672"
42627       ],
42628       [
42629         "North Korea (조선 민주주의 인민 공화국)",
42630         "kp",
42631         "850"
42632       ],
42633       [
42634         "Northern Mariana Islands",
42635         "mp",
42636         "1670"
42637       ],
42638       [
42639         "Norway (Norge)",
42640         "no",
42641         "47",
42642         0
42643       ],
42644       [
42645         "Oman (‫عُمان‬‎)",
42646         "om",
42647         "968"
42648       ],
42649       [
42650         "Pakistan (‫پاکستان‬‎)",
42651         "pk",
42652         "92"
42653       ],
42654       [
42655         "Palau",
42656         "pw",
42657         "680"
42658       ],
42659       [
42660         "Palestine (‫فلسطين‬‎)",
42661         "ps",
42662         "970"
42663       ],
42664       [
42665         "Panama (Panamá)",
42666         "pa",
42667         "507"
42668       ],
42669       [
42670         "Papua New Guinea",
42671         "pg",
42672         "675"
42673       ],
42674       [
42675         "Paraguay",
42676         "py",
42677         "595"
42678       ],
42679       [
42680         "Peru (Perú)",
42681         "pe",
42682         "51"
42683       ],
42684       [
42685         "Philippines",
42686         "ph",
42687         "63"
42688       ],
42689       [
42690         "Poland (Polska)",
42691         "pl",
42692         "48"
42693       ],
42694       [
42695         "Portugal",
42696         "pt",
42697         "351"
42698       ],
42699       [
42700         "Puerto Rico",
42701         "pr",
42702         "1",
42703         3,
42704         ["787", "939"]
42705       ],
42706       [
42707         "Qatar (‫قطر‬‎)",
42708         "qa",
42709         "974"
42710       ],
42711       [
42712         "Réunion (La Réunion)",
42713         "re",
42714         "262",
42715         0
42716       ],
42717       [
42718         "Romania (România)",
42719         "ro",
42720         "40"
42721       ],
42722       [
42723         "Russia (Россия)",
42724         "ru",
42725         "7",
42726         0
42727       ],
42728       [
42729         "Rwanda",
42730         "rw",
42731         "250"
42732       ],
42733       [
42734         "Saint Barthélemy",
42735         "bl",
42736         "590",
42737         1
42738       ],
42739       [
42740         "Saint Helena",
42741         "sh",
42742         "290"
42743       ],
42744       [
42745         "Saint Kitts and Nevis",
42746         "kn",
42747         "1869"
42748       ],
42749       [
42750         "Saint Lucia",
42751         "lc",
42752         "1758"
42753       ],
42754       [
42755         "Saint Martin (Saint-Martin (partie française))",
42756         "mf",
42757         "590",
42758         2
42759       ],
42760       [
42761         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42762         "pm",
42763         "508"
42764       ],
42765       [
42766         "Saint Vincent and the Grenadines",
42767         "vc",
42768         "1784"
42769       ],
42770       [
42771         "Samoa",
42772         "ws",
42773         "685"
42774       ],
42775       [
42776         "San Marino",
42777         "sm",
42778         "378"
42779       ],
42780       [
42781         "São Tomé and Príncipe (São Tomé e Príncipe)",
42782         "st",
42783         "239"
42784       ],
42785       [
42786         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42787         "sa",
42788         "966"
42789       ],
42790       [
42791         "Senegal (Sénégal)",
42792         "sn",
42793         "221"
42794       ],
42795       [
42796         "Serbia (Србија)",
42797         "rs",
42798         "381"
42799       ],
42800       [
42801         "Seychelles",
42802         "sc",
42803         "248"
42804       ],
42805       [
42806         "Sierra Leone",
42807         "sl",
42808         "232"
42809       ],
42810       [
42811         "Singapore",
42812         "sg",
42813         "65"
42814       ],
42815       [
42816         "Sint Maarten",
42817         "sx",
42818         "1721"
42819       ],
42820       [
42821         "Slovakia (Slovensko)",
42822         "sk",
42823         "421"
42824       ],
42825       [
42826         "Slovenia (Slovenija)",
42827         "si",
42828         "386"
42829       ],
42830       [
42831         "Solomon Islands",
42832         "sb",
42833         "677"
42834       ],
42835       [
42836         "Somalia (Soomaaliya)",
42837         "so",
42838         "252"
42839       ],
42840       [
42841         "South Africa",
42842         "za",
42843         "27"
42844       ],
42845       [
42846         "South Korea (대한민국)",
42847         "kr",
42848         "82"
42849       ],
42850       [
42851         "South Sudan (‫جنوب السودان‬‎)",
42852         "ss",
42853         "211"
42854       ],
42855       [
42856         "Spain (España)",
42857         "es",
42858         "34"
42859       ],
42860       [
42861         "Sri Lanka (ශ්‍රී ලංකාව)",
42862         "lk",
42863         "94"
42864       ],
42865       [
42866         "Sudan (‫السودان‬‎)",
42867         "sd",
42868         "249"
42869       ],
42870       [
42871         "Suriname",
42872         "sr",
42873         "597"
42874       ],
42875       [
42876         "Svalbard and Jan Mayen",
42877         "sj",
42878         "47",
42879         1
42880       ],
42881       [
42882         "Swaziland",
42883         "sz",
42884         "268"
42885       ],
42886       [
42887         "Sweden (Sverige)",
42888         "se",
42889         "46"
42890       ],
42891       [
42892         "Switzerland (Schweiz)",
42893         "ch",
42894         "41"
42895       ],
42896       [
42897         "Syria (‫سوريا‬‎)",
42898         "sy",
42899         "963"
42900       ],
42901       [
42902         "Taiwan (台灣)",
42903         "tw",
42904         "886"
42905       ],
42906       [
42907         "Tajikistan",
42908         "tj",
42909         "992"
42910       ],
42911       [
42912         "Tanzania",
42913         "tz",
42914         "255"
42915       ],
42916       [
42917         "Thailand (ไทย)",
42918         "th",
42919         "66"
42920       ],
42921       [
42922         "Timor-Leste",
42923         "tl",
42924         "670"
42925       ],
42926       [
42927         "Togo",
42928         "tg",
42929         "228"
42930       ],
42931       [
42932         "Tokelau",
42933         "tk",
42934         "690"
42935       ],
42936       [
42937         "Tonga",
42938         "to",
42939         "676"
42940       ],
42941       [
42942         "Trinidad and Tobago",
42943         "tt",
42944         "1868"
42945       ],
42946       [
42947         "Tunisia (‫تونس‬‎)",
42948         "tn",
42949         "216"
42950       ],
42951       [
42952         "Turkey (Türkiye)",
42953         "tr",
42954         "90"
42955       ],
42956       [
42957         "Turkmenistan",
42958         "tm",
42959         "993"
42960       ],
42961       [
42962         "Turks and Caicos Islands",
42963         "tc",
42964         "1649"
42965       ],
42966       [
42967         "Tuvalu",
42968         "tv",
42969         "688"
42970       ],
42971       [
42972         "U.S. Virgin Islands",
42973         "vi",
42974         "1340"
42975       ],
42976       [
42977         "Uganda",
42978         "ug",
42979         "256"
42980       ],
42981       [
42982         "Ukraine (Україна)",
42983         "ua",
42984         "380"
42985       ],
42986       [
42987         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42988         "ae",
42989         "971"
42990       ],
42991       [
42992         "United Kingdom",
42993         "gb",
42994         "44",
42995         0
42996       ],
42997       [
42998         "United States",
42999         "us",
43000         "1",
43001         0
43002       ],
43003       [
43004         "Uruguay",
43005         "uy",
43006         "598"
43007       ],
43008       [
43009         "Uzbekistan (Oʻzbekiston)",
43010         "uz",
43011         "998"
43012       ],
43013       [
43014         "Vanuatu",
43015         "vu",
43016         "678"
43017       ],
43018       [
43019         "Vatican City (Città del Vaticano)",
43020         "va",
43021         "39",
43022         1
43023       ],
43024       [
43025         "Venezuela",
43026         "ve",
43027         "58"
43028       ],
43029       [
43030         "Vietnam (Việt Nam)",
43031         "vn",
43032         "84"
43033       ],
43034       [
43035         "Wallis and Futuna (Wallis-et-Futuna)",
43036         "wf",
43037         "681"
43038       ],
43039       [
43040         "Western Sahara (‫الصحراء الغربية‬‎)",
43041         "eh",
43042         "212",
43043         1
43044       ],
43045       [
43046         "Yemen (‫اليمن‬‎)",
43047         "ye",
43048         "967"
43049       ],
43050       [
43051         "Zambia",
43052         "zm",
43053         "260"
43054       ],
43055       [
43056         "Zimbabwe",
43057         "zw",
43058         "263"
43059       ],
43060       [
43061         "Åland Islands",
43062         "ax",
43063         "358",
43064         1
43065       ]
43066   ];
43067   
43068   return d;
43069 }/**
43070 *    This script refer to:
43071 *    Title: International Telephone Input
43072 *    Author: Jack O'Connor
43073 *    Code version:  v12.1.12
43074 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43075 **/
43076
43077 /**
43078  * @class Roo.bootstrap.PhoneInput
43079  * @extends Roo.bootstrap.TriggerField
43080  * An input with International dial-code selection
43081  
43082  * @cfg {String} defaultDialCode default '+852'
43083  * @cfg {Array} preferedCountries default []
43084   
43085  * @constructor
43086  * Create a new PhoneInput.
43087  * @param {Object} config Configuration options
43088  */
43089
43090 Roo.bootstrap.PhoneInput = function(config) {
43091     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43092 };
43093
43094 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43095         
43096         listWidth: undefined,
43097         
43098         selectedClass: 'active',
43099         
43100         invalidClass : "has-warning",
43101         
43102         validClass: 'has-success',
43103         
43104         allowed: '0123456789',
43105         
43106         max_length: 15,
43107         
43108         /**
43109          * @cfg {String} defaultDialCode The default dial code when initializing the input
43110          */
43111         defaultDialCode: '+852',
43112         
43113         /**
43114          * @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
43115          */
43116         preferedCountries: false,
43117         
43118         getAutoCreate : function()
43119         {
43120             var data = Roo.bootstrap.PhoneInputData();
43121             var align = this.labelAlign || this.parentLabelAlign();
43122             var id = Roo.id();
43123             
43124             this.allCountries = [];
43125             this.dialCodeMapping = [];
43126             
43127             for (var i = 0; i < data.length; i++) {
43128               var c = data[i];
43129               this.allCountries[i] = {
43130                 name: c[0],
43131                 iso2: c[1],
43132                 dialCode: c[2],
43133                 priority: c[3] || 0,
43134                 areaCodes: c[4] || null
43135               };
43136               this.dialCodeMapping[c[2]] = {
43137                   name: c[0],
43138                   iso2: c[1],
43139                   priority: c[3] || 0,
43140                   areaCodes: c[4] || null
43141               };
43142             }
43143             
43144             var cfg = {
43145                 cls: 'form-group',
43146                 cn: []
43147             };
43148             
43149             var input =  {
43150                 tag: 'input',
43151                 id : id,
43152                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43153                 maxlength: this.max_length,
43154                 cls : 'form-control tel-input',
43155                 autocomplete: 'new-password'
43156             };
43157             
43158             var hiddenInput = {
43159                 tag: 'input',
43160                 type: 'hidden',
43161                 cls: 'hidden-tel-input'
43162             };
43163             
43164             if (this.name) {
43165                 hiddenInput.name = this.name;
43166             }
43167             
43168             if (this.disabled) {
43169                 input.disabled = true;
43170             }
43171             
43172             var flag_container = {
43173                 tag: 'div',
43174                 cls: 'flag-box',
43175                 cn: [
43176                     {
43177                         tag: 'div',
43178                         cls: 'flag'
43179                     },
43180                     {
43181                         tag: 'div',
43182                         cls: 'caret'
43183                     }
43184                 ]
43185             };
43186             
43187             var box = {
43188                 tag: 'div',
43189                 cls: this.hasFeedback ? 'has-feedback' : '',
43190                 cn: [
43191                     hiddenInput,
43192                     input,
43193                     {
43194                         tag: 'input',
43195                         cls: 'dial-code-holder',
43196                         disabled: true
43197                     }
43198                 ]
43199             };
43200             
43201             var container = {
43202                 cls: 'roo-select2-container input-group',
43203                 cn: [
43204                     flag_container,
43205                     box
43206                 ]
43207             };
43208             
43209             if (this.fieldLabel.length) {
43210                 var indicator = {
43211                     tag: 'i',
43212                     tooltip: 'This field is required'
43213                 };
43214                 
43215                 var label = {
43216                     tag: 'label',
43217                     'for':  id,
43218                     cls: 'control-label',
43219                     cn: []
43220                 };
43221                 
43222                 var label_text = {
43223                     tag: 'span',
43224                     html: this.fieldLabel
43225                 };
43226                 
43227                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43228                 label.cn = [
43229                     indicator,
43230                     label_text
43231                 ];
43232                 
43233                 if(this.indicatorpos == 'right') {
43234                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43235                     label.cn = [
43236                         label_text,
43237                         indicator
43238                     ];
43239                 }
43240                 
43241                 if(align == 'left') {
43242                     container = {
43243                         tag: 'div',
43244                         cn: [
43245                             container
43246                         ]
43247                     };
43248                     
43249                     if(this.labelWidth > 12){
43250                         label.style = "width: " + this.labelWidth + 'px';
43251                     }
43252                     if(this.labelWidth < 13 && this.labelmd == 0){
43253                         this.labelmd = this.labelWidth;
43254                     }
43255                     if(this.labellg > 0){
43256                         label.cls += ' col-lg-' + this.labellg;
43257                         input.cls += ' col-lg-' + (12 - this.labellg);
43258                     }
43259                     if(this.labelmd > 0){
43260                         label.cls += ' col-md-' + this.labelmd;
43261                         container.cls += ' col-md-' + (12 - this.labelmd);
43262                     }
43263                     if(this.labelsm > 0){
43264                         label.cls += ' col-sm-' + this.labelsm;
43265                         container.cls += ' col-sm-' + (12 - this.labelsm);
43266                     }
43267                     if(this.labelxs > 0){
43268                         label.cls += ' col-xs-' + this.labelxs;
43269                         container.cls += ' col-xs-' + (12 - this.labelxs);
43270                     }
43271                 }
43272             }
43273             
43274             cfg.cn = [
43275                 label,
43276                 container
43277             ];
43278             
43279             var settings = this;
43280             
43281             ['xs','sm','md','lg'].map(function(size){
43282                 if (settings[size]) {
43283                     cfg.cls += ' col-' + size + '-' + settings[size];
43284                 }
43285             });
43286             
43287             this.store = new Roo.data.Store({
43288                 proxy : new Roo.data.MemoryProxy({}),
43289                 reader : new Roo.data.JsonReader({
43290                     fields : [
43291                         {
43292                             'name' : 'name',
43293                             'type' : 'string'
43294                         },
43295                         {
43296                             'name' : 'iso2',
43297                             'type' : 'string'
43298                         },
43299                         {
43300                             'name' : 'dialCode',
43301                             'type' : 'string'
43302                         },
43303                         {
43304                             'name' : 'priority',
43305                             'type' : 'string'
43306                         },
43307                         {
43308                             'name' : 'areaCodes',
43309                             'type' : 'string'
43310                         }
43311                     ]
43312                 })
43313             });
43314             
43315             if(!this.preferedCountries) {
43316                 this.preferedCountries = [
43317                     'hk',
43318                     'gb',
43319                     'us'
43320                 ];
43321             }
43322             
43323             var p = this.preferedCountries.reverse();
43324             
43325             if(p) {
43326                 for (var i = 0; i < p.length; i++) {
43327                     for (var j = 0; j < this.allCountries.length; j++) {
43328                         if(this.allCountries[j].iso2 == p[i]) {
43329                             var t = this.allCountries[j];
43330                             this.allCountries.splice(j,1);
43331                             this.allCountries.unshift(t);
43332                         }
43333                     } 
43334                 }
43335             }
43336             
43337             this.store.proxy.data = {
43338                 success: true,
43339                 data: this.allCountries
43340             };
43341             
43342             return cfg;
43343         },
43344         
43345         initEvents : function()
43346         {
43347             this.createList();
43348             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43349             
43350             this.indicator = this.indicatorEl();
43351             this.flag = this.flagEl();
43352             this.dialCodeHolder = this.dialCodeHolderEl();
43353             
43354             this.trigger = this.el.select('div.flag-box',true).first();
43355             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43356             
43357             var _this = this;
43358             
43359             (function(){
43360                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43361                 _this.list.setWidth(lw);
43362             }).defer(100);
43363             
43364             this.list.on('mouseover', this.onViewOver, this);
43365             this.list.on('mousemove', this.onViewMove, this);
43366             this.inputEl().on("keyup", this.onKeyUp, this);
43367             this.inputEl().on("keypress", this.onKeyPress, this);
43368             
43369             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43370
43371             this.view = new Roo.View(this.list, this.tpl, {
43372                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43373             });
43374             
43375             this.view.on('click', this.onViewClick, this);
43376             this.setValue(this.defaultDialCode);
43377         },
43378         
43379         onTriggerClick : function(e)
43380         {
43381             Roo.log('trigger click');
43382             if(this.disabled){
43383                 return;
43384             }
43385             
43386             if(this.isExpanded()){
43387                 this.collapse();
43388                 this.hasFocus = false;
43389             }else {
43390                 this.store.load({});
43391                 this.hasFocus = true;
43392                 this.expand();
43393             }
43394         },
43395         
43396         isExpanded : function()
43397         {
43398             return this.list.isVisible();
43399         },
43400         
43401         collapse : function()
43402         {
43403             if(!this.isExpanded()){
43404                 return;
43405             }
43406             this.list.hide();
43407             Roo.get(document).un('mousedown', this.collapseIf, this);
43408             Roo.get(document).un('mousewheel', this.collapseIf, this);
43409             this.fireEvent('collapse', this);
43410             this.validate();
43411         },
43412         
43413         expand : function()
43414         {
43415             Roo.log('expand');
43416
43417             if(this.isExpanded() || !this.hasFocus){
43418                 return;
43419             }
43420             
43421             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43422             this.list.setWidth(lw);
43423             
43424             this.list.show();
43425             this.restrictHeight();
43426             
43427             Roo.get(document).on('mousedown', this.collapseIf, this);
43428             Roo.get(document).on('mousewheel', this.collapseIf, this);
43429             
43430             this.fireEvent('expand', this);
43431         },
43432         
43433         restrictHeight : function()
43434         {
43435             this.list.alignTo(this.inputEl(), this.listAlign);
43436             this.list.alignTo(this.inputEl(), this.listAlign);
43437         },
43438         
43439         onViewOver : function(e, t)
43440         {
43441             if(this.inKeyMode){
43442                 return;
43443             }
43444             var item = this.view.findItemFromChild(t);
43445             
43446             if(item){
43447                 var index = this.view.indexOf(item);
43448                 this.select(index, false);
43449             }
43450         },
43451
43452         // private
43453         onViewClick : function(view, doFocus, el, e)
43454         {
43455             var index = this.view.getSelectedIndexes()[0];
43456             
43457             var r = this.store.getAt(index);
43458             
43459             if(r){
43460                 this.onSelect(r, index);
43461             }
43462             if(doFocus !== false && !this.blockFocus){
43463                 this.inputEl().focus();
43464             }
43465         },
43466         
43467         onViewMove : function(e, t)
43468         {
43469             this.inKeyMode = false;
43470         },
43471         
43472         select : function(index, scrollIntoView)
43473         {
43474             this.selectedIndex = index;
43475             this.view.select(index);
43476             if(scrollIntoView !== false){
43477                 var el = this.view.getNode(index);
43478                 if(el){
43479                     this.list.scrollChildIntoView(el, false);
43480                 }
43481             }
43482         },
43483         
43484         createList : function()
43485         {
43486             this.list = Roo.get(document.body).createChild({
43487                 tag: 'ul',
43488                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43489                 style: 'display:none'
43490             });
43491             
43492             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43493         },
43494         
43495         collapseIf : function(e)
43496         {
43497             var in_combo  = e.within(this.el);
43498             var in_list =  e.within(this.list);
43499             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43500             
43501             if (in_combo || in_list || is_list) {
43502                 return;
43503             }
43504             this.collapse();
43505         },
43506         
43507         onSelect : function(record, index)
43508         {
43509             if(this.fireEvent('beforeselect', this, record, index) !== false){
43510                 
43511                 this.setFlagClass(record.data.iso2);
43512                 this.setDialCode(record.data.dialCode);
43513                 this.hasFocus = false;
43514                 this.collapse();
43515                 this.fireEvent('select', this, record, index);
43516             }
43517         },
43518         
43519         flagEl : function()
43520         {
43521             var flag = this.el.select('div.flag',true).first();
43522             if(!flag){
43523                 return false;
43524             }
43525             return flag;
43526         },
43527         
43528         dialCodeHolderEl : function()
43529         {
43530             var d = this.el.select('input.dial-code-holder',true).first();
43531             if(!d){
43532                 return false;
43533             }
43534             return d;
43535         },
43536         
43537         setDialCode : function(v)
43538         {
43539             this.dialCodeHolder.dom.value = '+'+v;
43540         },
43541         
43542         setFlagClass : function(n)
43543         {
43544             this.flag.dom.className = 'flag '+n;
43545         },
43546         
43547         getValue : function()
43548         {
43549             var v = this.inputEl().getValue();
43550             if(this.dialCodeHolder) {
43551                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43552             }
43553             return v;
43554         },
43555         
43556         setValue : function(v)
43557         {
43558             var d = this.getDialCode(v);
43559             
43560             //invalid dial code
43561             if(v.length == 0 || !d || d.length == 0) {
43562                 if(this.rendered){
43563                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43564                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43565                 }
43566                 return;
43567             }
43568             
43569             //valid dial code
43570             this.setFlagClass(this.dialCodeMapping[d].iso2);
43571             this.setDialCode(d);
43572             this.inputEl().dom.value = v.replace('+'+d,'');
43573             this.hiddenEl().dom.value = this.getValue();
43574             
43575             this.validate();
43576         },
43577         
43578         getDialCode : function(v)
43579         {
43580             v = v ||  '';
43581             
43582             if (v.length == 0) {
43583                 return this.dialCodeHolder.dom.value;
43584             }
43585             
43586             var dialCode = "";
43587             if (v.charAt(0) != "+") {
43588                 return false;
43589             }
43590             var numericChars = "";
43591             for (var i = 1; i < v.length; i++) {
43592               var c = v.charAt(i);
43593               if (!isNaN(c)) {
43594                 numericChars += c;
43595                 if (this.dialCodeMapping[numericChars]) {
43596                   dialCode = v.substr(1, i);
43597                 }
43598                 if (numericChars.length == 4) {
43599                   break;
43600                 }
43601               }
43602             }
43603             return dialCode;
43604         },
43605         
43606         reset : function()
43607         {
43608             this.setValue(this.defaultDialCode);
43609             this.validate();
43610         },
43611         
43612         hiddenEl : function()
43613         {
43614             return this.el.select('input.hidden-tel-input',true).first();
43615         },
43616         
43617         // after setting val
43618         onKeyUp : function(e){
43619             this.setValue(this.getValue());
43620         },
43621         
43622         onKeyPress : function(e){
43623             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43624                 e.stopEvent();
43625             }
43626         }
43627         
43628 });
43629 /**
43630  * @class Roo.bootstrap.MoneyField
43631  * @extends Roo.bootstrap.ComboBox
43632  * Bootstrap MoneyField class
43633  * 
43634  * @constructor
43635  * Create a new MoneyField.
43636  * @param {Object} config Configuration options
43637  */
43638
43639 Roo.bootstrap.MoneyField = function(config) {
43640     
43641     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43642     
43643 };
43644
43645 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43646     
43647     /**
43648      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43649      */
43650     allowDecimals : true,
43651     /**
43652      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43653      */
43654     decimalSeparator : ".",
43655     /**
43656      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43657      */
43658     decimalPrecision : 0,
43659     /**
43660      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43661      */
43662     allowNegative : true,
43663     /**
43664      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43665      */
43666     allowZero: true,
43667     /**
43668      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43669      */
43670     minValue : Number.NEGATIVE_INFINITY,
43671     /**
43672      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43673      */
43674     maxValue : Number.MAX_VALUE,
43675     /**
43676      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43677      */
43678     minText : "The minimum value for this field is {0}",
43679     /**
43680      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43681      */
43682     maxText : "The maximum value for this field is {0}",
43683     /**
43684      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43685      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43686      */
43687     nanText : "{0} is not a valid number",
43688     /**
43689      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43690      */
43691     castInt : true,
43692     /**
43693      * @cfg {String} defaults currency of the MoneyField
43694      * value should be in lkey
43695      */
43696     defaultCurrency : false,
43697     /**
43698      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43699      */
43700     thousandsDelimiter : false,
43701     /**
43702      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43703      */
43704     max_length: false,
43705     
43706     inputlg : 9,
43707     inputmd : 9,
43708     inputsm : 9,
43709     inputxs : 6,
43710     
43711     store : false,
43712     
43713     getAutoCreate : function()
43714     {
43715         var align = this.labelAlign || this.parentLabelAlign();
43716         
43717         var id = Roo.id();
43718
43719         var cfg = {
43720             cls: 'form-group',
43721             cn: []
43722         };
43723
43724         var input =  {
43725             tag: 'input',
43726             id : id,
43727             cls : 'form-control roo-money-amount-input',
43728             autocomplete: 'new-password'
43729         };
43730         
43731         var hiddenInput = {
43732             tag: 'input',
43733             type: 'hidden',
43734             id: Roo.id(),
43735             cls: 'hidden-number-input'
43736         };
43737         
43738         if(this.max_length) {
43739             input.maxlength = this.max_length; 
43740         }
43741         
43742         if (this.name) {
43743             hiddenInput.name = this.name;
43744         }
43745
43746         if (this.disabled) {
43747             input.disabled = true;
43748         }
43749
43750         var clg = 12 - this.inputlg;
43751         var cmd = 12 - this.inputmd;
43752         var csm = 12 - this.inputsm;
43753         var cxs = 12 - this.inputxs;
43754         
43755         var container = {
43756             tag : 'div',
43757             cls : 'row roo-money-field',
43758             cn : [
43759                 {
43760                     tag : 'div',
43761                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43762                     cn : [
43763                         {
43764                             tag : 'div',
43765                             cls: 'roo-select2-container input-group',
43766                             cn: [
43767                                 {
43768                                     tag : 'input',
43769                                     cls : 'form-control roo-money-currency-input',
43770                                     autocomplete: 'new-password',
43771                                     readOnly : 1,
43772                                     name : this.currencyName
43773                                 },
43774                                 {
43775                                     tag :'span',
43776                                     cls : 'input-group-addon',
43777                                     cn : [
43778                                         {
43779                                             tag: 'span',
43780                                             cls: 'caret'
43781                                         }
43782                                     ]
43783                                 }
43784                             ]
43785                         }
43786                     ]
43787                 },
43788                 {
43789                     tag : 'div',
43790                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43791                     cn : [
43792                         {
43793                             tag: 'div',
43794                             cls: this.hasFeedback ? 'has-feedback' : '',
43795                             cn: [
43796                                 input
43797                             ]
43798                         }
43799                     ]
43800                 }
43801             ]
43802             
43803         };
43804         
43805         if (this.fieldLabel.length) {
43806             var indicator = {
43807                 tag: 'i',
43808                 tooltip: 'This field is required'
43809             };
43810
43811             var label = {
43812                 tag: 'label',
43813                 'for':  id,
43814                 cls: 'control-label',
43815                 cn: []
43816             };
43817
43818             var label_text = {
43819                 tag: 'span',
43820                 html: this.fieldLabel
43821             };
43822
43823             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43824             label.cn = [
43825                 indicator,
43826                 label_text
43827             ];
43828
43829             if(this.indicatorpos == 'right') {
43830                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43831                 label.cn = [
43832                     label_text,
43833                     indicator
43834                 ];
43835             }
43836
43837             if(align == 'left') {
43838                 container = {
43839                     tag: 'div',
43840                     cn: [
43841                         container
43842                     ]
43843                 };
43844
43845                 if(this.labelWidth > 12){
43846                     label.style = "width: " + this.labelWidth + 'px';
43847                 }
43848                 if(this.labelWidth < 13 && this.labelmd == 0){
43849                     this.labelmd = this.labelWidth;
43850                 }
43851                 if(this.labellg > 0){
43852                     label.cls += ' col-lg-' + this.labellg;
43853                     input.cls += ' col-lg-' + (12 - this.labellg);
43854                 }
43855                 if(this.labelmd > 0){
43856                     label.cls += ' col-md-' + this.labelmd;
43857                     container.cls += ' col-md-' + (12 - this.labelmd);
43858                 }
43859                 if(this.labelsm > 0){
43860                     label.cls += ' col-sm-' + this.labelsm;
43861                     container.cls += ' col-sm-' + (12 - this.labelsm);
43862                 }
43863                 if(this.labelxs > 0){
43864                     label.cls += ' col-xs-' + this.labelxs;
43865                     container.cls += ' col-xs-' + (12 - this.labelxs);
43866                 }
43867             }
43868         }
43869
43870         cfg.cn = [
43871             label,
43872             container,
43873             hiddenInput
43874         ];
43875         
43876         var settings = this;
43877
43878         ['xs','sm','md','lg'].map(function(size){
43879             if (settings[size]) {
43880                 cfg.cls += ' col-' + size + '-' + settings[size];
43881             }
43882         });
43883         
43884         return cfg;
43885     },
43886     
43887     initEvents : function()
43888     {
43889         this.indicator = this.indicatorEl();
43890         
43891         this.initCurrencyEvent();
43892         
43893         this.initNumberEvent();
43894     },
43895     
43896     initCurrencyEvent : function()
43897     {
43898         if (!this.store) {
43899             throw "can not find store for combo";
43900         }
43901         
43902         this.store = Roo.factory(this.store, Roo.data);
43903         this.store.parent = this;
43904         
43905         this.createList();
43906         
43907         this.triggerEl = this.el.select('.input-group-addon', true).first();
43908         
43909         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43910         
43911         var _this = this;
43912         
43913         (function(){
43914             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43915             _this.list.setWidth(lw);
43916         }).defer(100);
43917         
43918         this.list.on('mouseover', this.onViewOver, this);
43919         this.list.on('mousemove', this.onViewMove, this);
43920         this.list.on('scroll', this.onViewScroll, this);
43921         
43922         if(!this.tpl){
43923             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43924         }
43925         
43926         this.view = new Roo.View(this.list, this.tpl, {
43927             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43928         });
43929         
43930         this.view.on('click', this.onViewClick, this);
43931         
43932         this.store.on('beforeload', this.onBeforeLoad, this);
43933         this.store.on('load', this.onLoad, this);
43934         this.store.on('loadexception', this.onLoadException, this);
43935         
43936         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43937             "up" : function(e){
43938                 this.inKeyMode = true;
43939                 this.selectPrev();
43940             },
43941
43942             "down" : function(e){
43943                 if(!this.isExpanded()){
43944                     this.onTriggerClick();
43945                 }else{
43946                     this.inKeyMode = true;
43947                     this.selectNext();
43948                 }
43949             },
43950
43951             "enter" : function(e){
43952                 this.collapse();
43953                 
43954                 if(this.fireEvent("specialkey", this, e)){
43955                     this.onViewClick(false);
43956                 }
43957                 
43958                 return true;
43959             },
43960
43961             "esc" : function(e){
43962                 this.collapse();
43963             },
43964
43965             "tab" : function(e){
43966                 this.collapse();
43967                 
43968                 if(this.fireEvent("specialkey", this, e)){
43969                     this.onViewClick(false);
43970                 }
43971                 
43972                 return true;
43973             },
43974
43975             scope : this,
43976
43977             doRelay : function(foo, bar, hname){
43978                 if(hname == 'down' || this.scope.isExpanded()){
43979                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43980                 }
43981                 return true;
43982             },
43983
43984             forceKeyDown: true
43985         });
43986         
43987         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43988         
43989     },
43990     
43991     initNumberEvent : function(e)
43992     {
43993         this.inputEl().on("keydown" , this.fireKey,  this);
43994         this.inputEl().on("focus", this.onFocus,  this);
43995         this.inputEl().on("blur", this.onBlur,  this);
43996         
43997         this.inputEl().relayEvent('keyup', this);
43998         
43999         if(this.indicator){
44000             this.indicator.addClass('invisible');
44001         }
44002  
44003         this.originalValue = this.getValue();
44004         
44005         if(this.validationEvent == 'keyup'){
44006             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44007             this.inputEl().on('keyup', this.filterValidation, this);
44008         }
44009         else if(this.validationEvent !== false){
44010             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44011         }
44012         
44013         if(this.selectOnFocus){
44014             this.on("focus", this.preFocus, this);
44015             
44016         }
44017         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44018             this.inputEl().on("keypress", this.filterKeys, this);
44019         } else {
44020             this.inputEl().relayEvent('keypress', this);
44021         }
44022         
44023         var allowed = "0123456789";
44024         
44025         if(this.allowDecimals){
44026             allowed += this.decimalSeparator;
44027         }
44028         
44029         if(this.allowNegative){
44030             allowed += "-";
44031         }
44032         
44033         if(this.thousandsDelimiter) {
44034             allowed += ",";
44035         }
44036         
44037         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44038         
44039         var keyPress = function(e){
44040             
44041             var k = e.getKey();
44042             
44043             var c = e.getCharCode();
44044             
44045             if(
44046                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44047                     allowed.indexOf(String.fromCharCode(c)) === -1
44048             ){
44049                 e.stopEvent();
44050                 return;
44051             }
44052             
44053             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44054                 return;
44055             }
44056             
44057             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44058                 e.stopEvent();
44059             }
44060         };
44061         
44062         this.inputEl().on("keypress", keyPress, this);
44063         
44064     },
44065     
44066     onTriggerClick : function(e)
44067     {   
44068         if(this.disabled){
44069             return;
44070         }
44071         
44072         this.page = 0;
44073         this.loadNext = false;
44074         
44075         if(this.isExpanded()){
44076             this.collapse();
44077             return;
44078         }
44079         
44080         this.hasFocus = true;
44081         
44082         if(this.triggerAction == 'all') {
44083             this.doQuery(this.allQuery, true);
44084             return;
44085         }
44086         
44087         this.doQuery(this.getRawValue());
44088     },
44089     
44090     getCurrency : function()
44091     {   
44092         var v = this.currencyEl().getValue();
44093         
44094         return v;
44095     },
44096     
44097     restrictHeight : function()
44098     {
44099         this.list.alignTo(this.currencyEl(), this.listAlign);
44100         this.list.alignTo(this.currencyEl(), this.listAlign);
44101     },
44102     
44103     onViewClick : function(view, doFocus, el, e)
44104     {
44105         var index = this.view.getSelectedIndexes()[0];
44106         
44107         var r = this.store.getAt(index);
44108         
44109         if(r){
44110             this.onSelect(r, index);
44111         }
44112     },
44113     
44114     onSelect : function(record, index){
44115         
44116         if(this.fireEvent('beforeselect', this, record, index) !== false){
44117         
44118             this.setFromCurrencyData(index > -1 ? record.data : false);
44119             
44120             this.collapse();
44121             
44122             this.fireEvent('select', this, record, index);
44123         }
44124     },
44125     
44126     setFromCurrencyData : function(o)
44127     {
44128         var currency = '';
44129         
44130         this.lastCurrency = o;
44131         
44132         if (this.currencyField) {
44133             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44134         } else {
44135             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44136         }
44137         
44138         this.lastSelectionText = currency;
44139         
44140         //setting default currency
44141         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44142             this.setCurrency(this.defaultCurrency);
44143             return;
44144         }
44145         
44146         this.setCurrency(currency);
44147     },
44148     
44149     setFromData : function(o)
44150     {
44151         var c = {};
44152         
44153         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44154         
44155         this.setFromCurrencyData(c);
44156         
44157         var value = '';
44158         
44159         if (this.name) {
44160             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44161         } else {
44162             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44163         }
44164         
44165         this.setValue(value);
44166         
44167     },
44168     
44169     setCurrency : function(v)
44170     {   
44171         this.currencyValue = v;
44172         
44173         if(this.rendered){
44174             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44175             this.validate();
44176         }
44177     },
44178     
44179     setValue : function(v)
44180     {
44181         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44182         
44183         this.value = v;
44184         
44185         if(this.rendered){
44186             
44187             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44188             
44189             this.inputEl().dom.value = (v == '') ? '' :
44190                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44191             
44192             if(!this.allowZero && v === '0') {
44193                 this.hiddenEl().dom.value = '';
44194                 this.inputEl().dom.value = '';
44195             }
44196             
44197             this.validate();
44198         }
44199     },
44200     
44201     getRawValue : function()
44202     {
44203         var v = this.inputEl().getValue();
44204         
44205         return v;
44206     },
44207     
44208     getValue : function()
44209     {
44210         return this.fixPrecision(this.parseValue(this.getRawValue()));
44211     },
44212     
44213     parseValue : function(value)
44214     {
44215         if(this.thousandsDelimiter) {
44216             value += "";
44217             r = new RegExp(",", "g");
44218             value = value.replace(r, "");
44219         }
44220         
44221         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44222         return isNaN(value) ? '' : value;
44223         
44224     },
44225     
44226     fixPrecision : function(value)
44227     {
44228         if(this.thousandsDelimiter) {
44229             value += "";
44230             r = new RegExp(",", "g");
44231             value = value.replace(r, "");
44232         }
44233         
44234         var nan = isNaN(value);
44235         
44236         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44237             return nan ? '' : value;
44238         }
44239         return parseFloat(value).toFixed(this.decimalPrecision);
44240     },
44241     
44242     decimalPrecisionFcn : function(v)
44243     {
44244         return Math.floor(v);
44245     },
44246     
44247     validateValue : function(value)
44248     {
44249         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44250             return false;
44251         }
44252         
44253         var num = this.parseValue(value);
44254         
44255         if(isNaN(num)){
44256             this.markInvalid(String.format(this.nanText, value));
44257             return false;
44258         }
44259         
44260         if(num < this.minValue){
44261             this.markInvalid(String.format(this.minText, this.minValue));
44262             return false;
44263         }
44264         
44265         if(num > this.maxValue){
44266             this.markInvalid(String.format(this.maxText, this.maxValue));
44267             return false;
44268         }
44269         
44270         return true;
44271     },
44272     
44273     validate : function()
44274     {
44275         if(this.disabled || this.allowBlank){
44276             this.markValid();
44277             return true;
44278         }
44279         
44280         var currency = this.getCurrency();
44281         
44282         if(this.validateValue(this.getRawValue()) && currency.length){
44283             this.markValid();
44284             return true;
44285         }
44286         
44287         this.markInvalid();
44288         return false;
44289     },
44290     
44291     getName: function()
44292     {
44293         return this.name;
44294     },
44295     
44296     beforeBlur : function()
44297     {
44298         if(!this.castInt){
44299             return;
44300         }
44301         
44302         var v = this.parseValue(this.getRawValue());
44303         
44304         if(v || v == 0){
44305             this.setValue(v);
44306         }
44307     },
44308     
44309     onBlur : function()
44310     {
44311         this.beforeBlur();
44312         
44313         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44314             //this.el.removeClass(this.focusClass);
44315         }
44316         
44317         this.hasFocus = false;
44318         
44319         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44320             this.validate();
44321         }
44322         
44323         var v = this.getValue();
44324         
44325         if(String(v) !== String(this.startValue)){
44326             this.fireEvent('change', this, v, this.startValue);
44327         }
44328         
44329         this.fireEvent("blur", this);
44330     },
44331     
44332     inputEl : function()
44333     {
44334         return this.el.select('.roo-money-amount-input', true).first();
44335     },
44336     
44337     currencyEl : function()
44338     {
44339         return this.el.select('.roo-money-currency-input', true).first();
44340     },
44341     
44342     hiddenEl : function()
44343     {
44344         return this.el.select('input.hidden-number-input',true).first();
44345     }
44346     
44347 });/**
44348  * @class Roo.bootstrap.BezierSignature
44349  * @extends Roo.bootstrap.Component
44350  * Bootstrap BezierSignature class
44351  * This script refer to:
44352  *    Title: Signature Pad
44353  *    Author: szimek
44354  *    Availability: https://github.com/szimek/signature_pad
44355  *
44356  * @constructor
44357  * Create a new BezierSignature
44358  * @param {Object} config The config object
44359  */
44360
44361 Roo.bootstrap.BezierSignature = function(config){
44362     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44363     this.addEvents({
44364         "resize" : true
44365     });
44366 };
44367
44368 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44369 {
44370      
44371     curve_data: [],
44372     
44373     is_empty: true,
44374     
44375     mouse_btn_down: true,
44376     
44377     /**
44378      * @cfg {int} canvas height
44379      */
44380     canvas_height: '200px',
44381     
44382     /**
44383      * @cfg {float|function} Radius of a single dot.
44384      */ 
44385     dot_size: false,
44386     
44387     /**
44388      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44389      */
44390     min_width: 0.5,
44391     
44392     /**
44393      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44394      */
44395     max_width: 2.5,
44396     
44397     /**
44398      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44399      */
44400     throttle: 16,
44401     
44402     /**
44403      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44404      */
44405     min_distance: 5,
44406     
44407     /**
44408      * @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.
44409      */
44410     bg_color: 'rgba(0, 0, 0, 0)',
44411     
44412     /**
44413      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44414      */
44415     dot_color: 'black',
44416     
44417     /**
44418      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44419      */ 
44420     velocity_filter_weight: 0.7,
44421     
44422     /**
44423      * @cfg {function} Callback when stroke begin. 
44424      */
44425     onBegin: false,
44426     
44427     /**
44428      * @cfg {function} Callback when stroke end.
44429      */
44430     onEnd: false,
44431     
44432     getAutoCreate : function()
44433     {
44434         var cls = 'roo-signature column';
44435         
44436         if(this.cls){
44437             cls += ' ' + this.cls;
44438         }
44439         
44440         var col_sizes = [
44441             'lg',
44442             'md',
44443             'sm',
44444             'xs'
44445         ];
44446         
44447         for(var i = 0; i < col_sizes.length; i++) {
44448             if(this[col_sizes[i]]) {
44449                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44450             }
44451         }
44452         
44453         var cfg = {
44454             tag: 'div',
44455             cls: cls,
44456             cn: [
44457                 {
44458                     tag: 'div',
44459                     cls: 'roo-signature-body',
44460                     cn: [
44461                         {
44462                             tag: 'canvas',
44463                             cls: 'roo-signature-body-canvas',
44464                             height: this.canvas_height,
44465                             width: this.canvas_width
44466                         }
44467                     ]
44468                 },
44469                 {
44470                     tag: 'input',
44471                     type: 'file',
44472                     style: 'display: none'
44473                 }
44474             ]
44475         };
44476         
44477         return cfg;
44478     },
44479     
44480     initEvents: function() 
44481     {
44482         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44483         
44484         var canvas = this.canvasEl();
44485         
44486         // mouse && touch event swapping...
44487         canvas.dom.style.touchAction = 'none';
44488         canvas.dom.style.msTouchAction = 'none';
44489         
44490         this.mouse_btn_down = false;
44491         canvas.on('mousedown', this._handleMouseDown, this);
44492         canvas.on('mousemove', this._handleMouseMove, this);
44493         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44494         
44495         if (window.PointerEvent) {
44496             canvas.on('pointerdown', this._handleMouseDown, this);
44497             canvas.on('pointermove', this._handleMouseMove, this);
44498             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44499         }
44500         
44501         if ('ontouchstart' in window) {
44502             canvas.on('touchstart', this._handleTouchStart, this);
44503             canvas.on('touchmove', this._handleTouchMove, this);
44504             canvas.on('touchend', this._handleTouchEnd, this);
44505         }
44506         
44507         Roo.EventManager.onWindowResize(this.resize, this, true);
44508         
44509         // file input event
44510         this.fileEl().on('change', this.uploadImage, this);
44511         
44512         this.clear();
44513         
44514         this.resize();
44515     },
44516     
44517     resize: function(){
44518         
44519         var canvas = this.canvasEl().dom;
44520         var ctx = this.canvasElCtx();
44521         var img_data = false;
44522         
44523         if(canvas.width > 0) {
44524             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44525         }
44526         // setting canvas width will clean img data
44527         canvas.width = 0;
44528         
44529         var style = window.getComputedStyle ? 
44530             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44531             
44532         var padding_left = parseInt(style.paddingLeft) || 0;
44533         var padding_right = parseInt(style.paddingRight) || 0;
44534         
44535         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44536         
44537         if(img_data) {
44538             ctx.putImageData(img_data, 0, 0);
44539         }
44540     },
44541     
44542     _handleMouseDown: function(e)
44543     {
44544         if (e.browserEvent.which === 1) {
44545             this.mouse_btn_down = true;
44546             this.strokeBegin(e);
44547         }
44548     },
44549     
44550     _handleMouseMove: function (e)
44551     {
44552         if (this.mouse_btn_down) {
44553             this.strokeMoveUpdate(e);
44554         }
44555     },
44556     
44557     _handleMouseUp: function (e)
44558     {
44559         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44560             this.mouse_btn_down = false;
44561             this.strokeEnd(e);
44562         }
44563     },
44564     
44565     _handleTouchStart: function (e) {
44566         
44567         e.preventDefault();
44568         if (e.browserEvent.targetTouches.length === 1) {
44569             // var touch = e.browserEvent.changedTouches[0];
44570             // this.strokeBegin(touch);
44571             
44572              this.strokeBegin(e); // assume e catching the correct xy...
44573         }
44574     },
44575     
44576     _handleTouchMove: function (e) {
44577         e.preventDefault();
44578         // var touch = event.targetTouches[0];
44579         // _this._strokeMoveUpdate(touch);
44580         this.strokeMoveUpdate(e);
44581     },
44582     
44583     _handleTouchEnd: function (e) {
44584         var wasCanvasTouched = e.target === this.canvasEl().dom;
44585         if (wasCanvasTouched) {
44586             e.preventDefault();
44587             // var touch = event.changedTouches[0];
44588             // _this._strokeEnd(touch);
44589             this.strokeEnd(e);
44590         }
44591     },
44592     
44593     reset: function () {
44594         this._lastPoints = [];
44595         this._lastVelocity = 0;
44596         this._lastWidth = (this.min_width + this.max_width) / 2;
44597         this.canvasElCtx().fillStyle = this.dot_color;
44598     },
44599     
44600     strokeMoveUpdate: function(e)
44601     {
44602         this.strokeUpdate(e);
44603         
44604         if (this.throttle) {
44605             this.throttleStroke(this.strokeUpdate, this.throttle);
44606         }
44607         else {
44608             this.strokeUpdate(e);
44609         }
44610     },
44611     
44612     strokeBegin: function(e)
44613     {
44614         var newPointGroup = {
44615             color: this.dot_color,
44616             points: []
44617         };
44618         
44619         if (typeof this.onBegin === 'function') {
44620             this.onBegin(e);
44621         }
44622         
44623         this.curve_data.push(newPointGroup);
44624         this.reset();
44625         this.strokeUpdate(e);
44626     },
44627     
44628     strokeUpdate: function(e)
44629     {
44630         var rect = this.canvasEl().dom.getBoundingClientRect();
44631         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44632         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44633         var lastPoints = lastPointGroup.points;
44634         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44635         var isLastPointTooClose = lastPoint
44636             ? point.distanceTo(lastPoint) <= this.min_distance
44637             : false;
44638         var color = lastPointGroup.color;
44639         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44640             var curve = this.addPoint(point);
44641             if (!lastPoint) {
44642                 this.drawDot({color: color, point: point});
44643             }
44644             else if (curve) {
44645                 this.drawCurve({color: color, curve: curve});
44646             }
44647             lastPoints.push({
44648                 time: point.time,
44649                 x: point.x,
44650                 y: point.y
44651             });
44652         }
44653     },
44654     
44655     strokeEnd: function(e)
44656     {
44657         this.strokeUpdate(e);
44658         if (typeof this.onEnd === 'function') {
44659             this.onEnd(e);
44660         }
44661     },
44662     
44663     addPoint:  function (point) {
44664         var _lastPoints = this._lastPoints;
44665         _lastPoints.push(point);
44666         if (_lastPoints.length > 2) {
44667             if (_lastPoints.length === 3) {
44668                 _lastPoints.unshift(_lastPoints[0]);
44669             }
44670             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44671             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44672             _lastPoints.shift();
44673             return curve;
44674         }
44675         return null;
44676     },
44677     
44678     calculateCurveWidths: function (startPoint, endPoint) {
44679         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44680             (1 - this.velocity_filter_weight) * this._lastVelocity;
44681
44682         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44683         var widths = {
44684             end: newWidth,
44685             start: this._lastWidth
44686         };
44687         
44688         this._lastVelocity = velocity;
44689         this._lastWidth = newWidth;
44690         return widths;
44691     },
44692     
44693     drawDot: function (_a) {
44694         var color = _a.color, point = _a.point;
44695         var ctx = this.canvasElCtx();
44696         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44697         ctx.beginPath();
44698         this.drawCurveSegment(point.x, point.y, width);
44699         ctx.closePath();
44700         ctx.fillStyle = color;
44701         ctx.fill();
44702     },
44703     
44704     drawCurve: function (_a) {
44705         var color = _a.color, curve = _a.curve;
44706         var ctx = this.canvasElCtx();
44707         var widthDelta = curve.endWidth - curve.startWidth;
44708         var drawSteps = Math.floor(curve.length()) * 2;
44709         ctx.beginPath();
44710         ctx.fillStyle = color;
44711         for (var i = 0; i < drawSteps; i += 1) {
44712         var t = i / drawSteps;
44713         var tt = t * t;
44714         var ttt = tt * t;
44715         var u = 1 - t;
44716         var uu = u * u;
44717         var uuu = uu * u;
44718         var x = uuu * curve.startPoint.x;
44719         x += 3 * uu * t * curve.control1.x;
44720         x += 3 * u * tt * curve.control2.x;
44721         x += ttt * curve.endPoint.x;
44722         var y = uuu * curve.startPoint.y;
44723         y += 3 * uu * t * curve.control1.y;
44724         y += 3 * u * tt * curve.control2.y;
44725         y += ttt * curve.endPoint.y;
44726         var width = curve.startWidth + ttt * widthDelta;
44727         this.drawCurveSegment(x, y, width);
44728         }
44729         ctx.closePath();
44730         ctx.fill();
44731     },
44732     
44733     drawCurveSegment: function (x, y, width) {
44734         var ctx = this.canvasElCtx();
44735         ctx.moveTo(x, y);
44736         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44737         this.is_empty = false;
44738     },
44739     
44740     clear: function()
44741     {
44742         var ctx = this.canvasElCtx();
44743         var canvas = this.canvasEl().dom;
44744         ctx.fillStyle = this.bg_color;
44745         ctx.clearRect(0, 0, canvas.width, canvas.height);
44746         ctx.fillRect(0, 0, canvas.width, canvas.height);
44747         this.curve_data = [];
44748         this.reset();
44749         this.is_empty = true;
44750     },
44751     
44752     fileEl: function()
44753     {
44754         return  this.el.select('input',true).first();
44755     },
44756     
44757     canvasEl: function()
44758     {
44759         return this.el.select('canvas',true).first();
44760     },
44761     
44762     canvasElCtx: function()
44763     {
44764         return this.el.select('canvas',true).first().dom.getContext('2d');
44765     },
44766     
44767     getImage: function(type)
44768     {
44769         if(this.is_empty) {
44770             return false;
44771         }
44772         
44773         // encryption ?
44774         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44775     },
44776     
44777     drawFromImage: function(img_src)
44778     {
44779         var img = new Image();
44780         
44781         img.onload = function(){
44782             this.canvasElCtx().drawImage(img, 0, 0);
44783         }.bind(this);
44784         
44785         img.src = img_src;
44786         
44787         this.is_empty = false;
44788     },
44789     
44790     selectImage: function()
44791     {
44792         this.fileEl().dom.click();
44793     },
44794     
44795     uploadImage: function(e)
44796     {
44797         var reader = new FileReader();
44798         
44799         reader.onload = function(e){
44800             var img = new Image();
44801             img.onload = function(){
44802                 this.reset();
44803                 this.canvasElCtx().drawImage(img, 0, 0);
44804             }.bind(this);
44805             img.src = e.target.result;
44806         }.bind(this);
44807         
44808         reader.readAsDataURL(e.target.files[0]);
44809     },
44810     
44811     // Bezier Point Constructor
44812     Point: (function () {
44813         function Point(x, y, time) {
44814             this.x = x;
44815             this.y = y;
44816             this.time = time || Date.now();
44817         }
44818         Point.prototype.distanceTo = function (start) {
44819             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44820         };
44821         Point.prototype.equals = function (other) {
44822             return this.x === other.x && this.y === other.y && this.time === other.time;
44823         };
44824         Point.prototype.velocityFrom = function (start) {
44825             return this.time !== start.time
44826             ? this.distanceTo(start) / (this.time - start.time)
44827             : 0;
44828         };
44829         return Point;
44830     }()),
44831     
44832     
44833     // Bezier Constructor
44834     Bezier: (function () {
44835         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44836             this.startPoint = startPoint;
44837             this.control2 = control2;
44838             this.control1 = control1;
44839             this.endPoint = endPoint;
44840             this.startWidth = startWidth;
44841             this.endWidth = endWidth;
44842         }
44843         Bezier.fromPoints = function (points, widths, scope) {
44844             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44845             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44846             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44847         };
44848         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44849             var dx1 = s1.x - s2.x;
44850             var dy1 = s1.y - s2.y;
44851             var dx2 = s2.x - s3.x;
44852             var dy2 = s2.y - s3.y;
44853             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44854             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44855             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44856             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44857             var dxm = m1.x - m2.x;
44858             var dym = m1.y - m2.y;
44859             var k = l2 / (l1 + l2);
44860             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44861             var tx = s2.x - cm.x;
44862             var ty = s2.y - cm.y;
44863             return {
44864                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44865                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44866             };
44867         };
44868         Bezier.prototype.length = function () {
44869             var steps = 10;
44870             var length = 0;
44871             var px;
44872             var py;
44873             for (var i = 0; i <= steps; i += 1) {
44874                 var t = i / steps;
44875                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44876                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44877                 if (i > 0) {
44878                     var xdiff = cx - px;
44879                     var ydiff = cy - py;
44880                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44881                 }
44882                 px = cx;
44883                 py = cy;
44884             }
44885             return length;
44886         };
44887         Bezier.prototype.point = function (t, start, c1, c2, end) {
44888             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44889             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44890             + (3.0 * c2 * (1.0 - t) * t * t)
44891             + (end * t * t * t);
44892         };
44893         return Bezier;
44894     }()),
44895     
44896     throttleStroke: function(fn, wait) {
44897       if (wait === void 0) { wait = 250; }
44898       var previous = 0;
44899       var timeout = null;
44900       var result;
44901       var storedContext;
44902       var storedArgs;
44903       var later = function () {
44904           previous = Date.now();
44905           timeout = null;
44906           result = fn.apply(storedContext, storedArgs);
44907           if (!timeout) {
44908               storedContext = null;
44909               storedArgs = [];
44910           }
44911       };
44912       return function wrapper() {
44913           var args = [];
44914           for (var _i = 0; _i < arguments.length; _i++) {
44915               args[_i] = arguments[_i];
44916           }
44917           var now = Date.now();
44918           var remaining = wait - (now - previous);
44919           storedContext = this;
44920           storedArgs = args;
44921           if (remaining <= 0 || remaining > wait) {
44922               if (timeout) {
44923                   clearTimeout(timeout);
44924                   timeout = null;
44925               }
44926               previous = now;
44927               result = fn.apply(storedContext, storedArgs);
44928               if (!timeout) {
44929                   storedContext = null;
44930                   storedArgs = [];
44931               }
44932           }
44933           else if (!timeout) {
44934               timeout = window.setTimeout(later, remaining);
44935           }
44936           return result;
44937       };
44938   }
44939   
44940 });
44941
44942  
44943
44944