c36e16cc8dfb80daa6ddb4a14747b67dd3bdc412
[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         
8458     onDblClick : function(e,el)
8459     {
8460         var cell = Roo.get(el);
8461         
8462         if(!cell || (!this.cellSelection && !this.rowSelection)){
8463             return;
8464         }
8465         
8466         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467             cell = cell.findParent('td', false, true);
8468         }
8469         
8470         if(!cell || typeof(cell) == 'undefined'){
8471             return;
8472         }
8473         
8474         var row = cell.findParent('tr', false, true);
8475         
8476         if(!row || typeof(row) == 'undefined'){
8477             return;
8478         }
8479         
8480         var cellIndex = cell.dom.cellIndex;
8481         var rowIndex = this.getRowIndex(row);
8482         
8483         if(this.cellSelection){
8484             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8485         }
8486         
8487         if(this.rowSelection){
8488             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8489         }
8490     },
8491     
8492     sort : function(e,el)
8493     {
8494         var col = Roo.get(el);
8495         
8496         if(!col.hasClass('sortable')){
8497             return;
8498         }
8499         
8500         var sort = col.attr('sort');
8501         var dir = 'ASC';
8502         
8503         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8504             dir = 'DESC';
8505         }
8506         
8507         this.store.sortInfo = {field : sort, direction : dir};
8508         
8509         if (this.footer) {
8510             Roo.log("calling footer first");
8511             this.footer.onClick('first');
8512         } else {
8513         
8514             this.store.load({ params : { start : 0 } });
8515         }
8516     },
8517     
8518     renderHeader : function()
8519     {
8520         var header = {
8521             tag: 'thead',
8522             cn : []
8523         };
8524         
8525         var cm = this.cm;
8526         this.totalWidth = 0;
8527         
8528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529             
8530             var config = cm.config[i];
8531             
8532             var c = {
8533                 tag: 'th',
8534                 cls : 'x-hcol-' + i,
8535                 style : '',
8536                 tooltip : cm.getColumnTooltip(i) || '',
8537                 html: cm.getColumnHeader(i)
8538             };
8539             
8540             var hh = '';
8541             
8542             if(typeof(config.sortable) != 'undefined' && config.sortable){
8543                 c.cls = 'sortable';
8544                 c.html = '<i class="fa"></i>' + c.html;
8545             }
8546             
8547             // could use BS4 hidden-..-down 
8548             
8549             if(typeof(config.lgHeader) != 'undefined'){
8550                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8551             }
8552             
8553             if(typeof(config.mdHeader) != 'undefined'){
8554                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8555             }
8556             
8557             if(typeof(config.smHeader) != 'undefined'){
8558                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8559             }
8560             
8561             if(typeof(config.xsHeader) != 'undefined'){
8562                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8563             }
8564             
8565             if(hh.length){
8566                 c.html = hh;
8567             }
8568             
8569             if(typeof(config.tooltip) != 'undefined'){
8570                 c.tooltip = config.tooltip;
8571             }
8572             
8573             if(typeof(config.colspan) != 'undefined'){
8574                 c.colspan = config.colspan;
8575             }
8576             
8577             if(typeof(config.hidden) != 'undefined' && config.hidden){
8578                 c.style += ' display:none;';
8579             }
8580             
8581             if(typeof(config.dataIndex) != 'undefined'){
8582                 c.sort = config.dataIndex;
8583             }
8584             
8585            
8586             
8587             if(typeof(config.align) != 'undefined' && config.align.length){
8588                 c.style += ' text-align:' + config.align + ';';
8589             }
8590             
8591             if(typeof(config.width) != 'undefined'){
8592                 c.style += ' width:' + config.width + 'px;';
8593                 this.totalWidth += config.width;
8594             } else {
8595                 this.totalWidth += 100; // assume minimum of 100 per column?
8596             }
8597             
8598             if(typeof(config.cls) != 'undefined'){
8599                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8600             }
8601             
8602             ['xs','sm','md','lg'].map(function(size){
8603                 
8604                 if(typeof(config[size]) == 'undefined'){
8605                     return;
8606                 }
8607                  
8608                 if (!config[size]) { // 0 = hidden
8609                     // BS 4 '0' is treated as hide that column and below.
8610                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8611                     return;
8612                 }
8613                 
8614                 c.cls += ' col-' + size + '-' + config[size] + (
8615                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8616                 );
8617                 
8618                 
8619             });
8620             
8621             header.cn.push(c)
8622         }
8623         
8624         return header;
8625     },
8626     
8627     renderBody : function()
8628     {
8629         var body = {
8630             tag: 'tbody',
8631             cn : [
8632                 {
8633                     tag: 'tr',
8634                     cn : [
8635                         {
8636                             tag : 'td',
8637                             colspan :  this.cm.getColumnCount()
8638                         }
8639                     ]
8640                 }
8641             ]
8642         };
8643         
8644         return body;
8645     },
8646     
8647     renderFooter : function()
8648     {
8649         var footer = {
8650             tag: 'tfoot',
8651             cn : [
8652                 {
8653                     tag: 'tr',
8654                     cn : [
8655                         {
8656                             tag : 'td',
8657                             colspan :  this.cm.getColumnCount()
8658                         }
8659                     ]
8660                 }
8661             ]
8662         };
8663         
8664         return footer;
8665     },
8666     
8667     
8668     
8669     onLoad : function()
8670     {
8671 //        Roo.log('ds onload');
8672         this.clear();
8673         
8674         var _this = this;
8675         var cm = this.cm;
8676         var ds = this.store;
8677         
8678         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8679             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8680             if (_this.store.sortInfo) {
8681                     
8682                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8683                     e.select('i', true).addClass(['fa-arrow-up']);
8684                 }
8685                 
8686                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8687                     e.select('i', true).addClass(['fa-arrow-down']);
8688                 }
8689             }
8690         });
8691         
8692         var tbody =  this.mainBody;
8693               
8694         if(ds.getCount() > 0){
8695             ds.data.each(function(d,rowIndex){
8696                 var row =  this.renderRow(cm, ds, rowIndex);
8697                 
8698                 tbody.createChild(row);
8699                 
8700                 var _this = this;
8701                 
8702                 if(row.cellObjects.length){
8703                     Roo.each(row.cellObjects, function(r){
8704                         _this.renderCellObject(r);
8705                     })
8706                 }
8707                 
8708             }, this);
8709         }
8710         
8711         var tfoot = this.el.select('tfoot', true).first();
8712         
8713         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8714             
8715             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8716             
8717             var total = this.ds.getTotalCount();
8718             
8719             if(this.footer.pageSize < total){
8720                 this.mainFoot.show();
8721             }
8722         }
8723         
8724         Roo.each(this.el.select('tbody td', true).elements, function(e){
8725             e.on('mouseover', _this.onMouseover, _this);
8726         });
8727         
8728         Roo.each(this.el.select('tbody td', true).elements, function(e){
8729             e.on('mouseout', _this.onMouseout, _this);
8730         });
8731         this.fireEvent('rowsrendered', this);
8732         
8733         this.autoSize();
8734     },
8735     
8736     
8737     onUpdate : function(ds,record)
8738     {
8739         this.refreshRow(record);
8740         this.autoSize();
8741     },
8742     
8743     onRemove : function(ds, record, index, isUpdate){
8744         if(isUpdate !== true){
8745             this.fireEvent("beforerowremoved", this, index, record);
8746         }
8747         var bt = this.mainBody.dom;
8748         
8749         var rows = this.el.select('tbody > tr', true).elements;
8750         
8751         if(typeof(rows[index]) != 'undefined'){
8752             bt.removeChild(rows[index].dom);
8753         }
8754         
8755 //        if(bt.rows[index]){
8756 //            bt.removeChild(bt.rows[index]);
8757 //        }
8758         
8759         if(isUpdate !== true){
8760             //this.stripeRows(index);
8761             //this.syncRowHeights(index, index);
8762             //this.layout();
8763             this.fireEvent("rowremoved", this, index, record);
8764         }
8765     },
8766     
8767     onAdd : function(ds, records, rowIndex)
8768     {
8769         //Roo.log('on Add called');
8770         // - note this does not handle multiple adding very well..
8771         var bt = this.mainBody.dom;
8772         for (var i =0 ; i < records.length;i++) {
8773             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8774             //Roo.log(records[i]);
8775             //Roo.log(this.store.getAt(rowIndex+i));
8776             this.insertRow(this.store, rowIndex + i, false);
8777             return;
8778         }
8779         
8780     },
8781     
8782     
8783     refreshRow : function(record){
8784         var ds = this.store, index;
8785         if(typeof record == 'number'){
8786             index = record;
8787             record = ds.getAt(index);
8788         }else{
8789             index = ds.indexOf(record);
8790             if (index < 0) {
8791                 return; // should not happen - but seems to 
8792             }
8793         }
8794         this.insertRow(ds, index, true);
8795         this.autoSize();
8796         this.onRemove(ds, record, index+1, true);
8797         this.autoSize();
8798         //this.syncRowHeights(index, index);
8799         //this.layout();
8800         this.fireEvent("rowupdated", this, index, record);
8801     },
8802     
8803     insertRow : function(dm, rowIndex, isUpdate){
8804         
8805         if(!isUpdate){
8806             this.fireEvent("beforerowsinserted", this, rowIndex);
8807         }
8808             //var s = this.getScrollState();
8809         var row = this.renderRow(this.cm, this.store, rowIndex);
8810         // insert before rowIndex..
8811         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8812         
8813         var _this = this;
8814                 
8815         if(row.cellObjects.length){
8816             Roo.each(row.cellObjects, function(r){
8817                 _this.renderCellObject(r);
8818             })
8819         }
8820             
8821         if(!isUpdate){
8822             this.fireEvent("rowsinserted", this, rowIndex);
8823             //this.syncRowHeights(firstRow, lastRow);
8824             //this.stripeRows(firstRow);
8825             //this.layout();
8826         }
8827         
8828     },
8829     
8830     
8831     getRowDom : function(rowIndex)
8832     {
8833         var rows = this.el.select('tbody > tr', true).elements;
8834         
8835         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8836         
8837     },
8838     // returns the object tree for a tr..
8839   
8840     
8841     renderRow : function(cm, ds, rowIndex) 
8842     {
8843         var d = ds.getAt(rowIndex);
8844         
8845         var row = {
8846             tag : 'tr',
8847             cls : 'x-row-' + rowIndex,
8848             cn : []
8849         };
8850             
8851         var cellObjects = [];
8852         
8853         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8854             var config = cm.config[i];
8855             
8856             var renderer = cm.getRenderer(i);
8857             var value = '';
8858             var id = false;
8859             
8860             if(typeof(renderer) !== 'undefined'){
8861                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8862             }
8863             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8864             // and are rendered into the cells after the row is rendered - using the id for the element.
8865             
8866             if(typeof(value) === 'object'){
8867                 id = Roo.id();
8868                 cellObjects.push({
8869                     container : id,
8870                     cfg : value 
8871                 })
8872             }
8873             
8874             var rowcfg = {
8875                 record: d,
8876                 rowIndex : rowIndex,
8877                 colIndex : i,
8878                 rowClass : ''
8879             };
8880
8881             this.fireEvent('rowclass', this, rowcfg);
8882             
8883             var td = {
8884                 tag: 'td',
8885                 // this might end up displaying HTML?
8886                 // this is too messy... - better to only do it on columsn you know are going to be too long
8887                 //tooltip : (typeof(value) === 'object') ? '' : value,
8888                 cls : rowcfg.rowClass + ' x-col-' + i,
8889                 style: '',
8890                 html: (typeof(value) === 'object') ? '' : value
8891             };
8892             
8893             if (id) {
8894                 td.id = id;
8895             }
8896             
8897             if(typeof(config.colspan) != 'undefined'){
8898                 td.colspan = config.colspan;
8899             }
8900             
8901             if(typeof(config.hidden) != 'undefined' && config.hidden){
8902                 td.style += ' display:none;';
8903             }
8904             
8905             if(typeof(config.align) != 'undefined' && config.align.length){
8906                 td.style += ' text-align:' + config.align + ';';
8907             }
8908             if(typeof(config.valign) != 'undefined' && config.valign.length){
8909                 td.style += ' vertical-align:' + config.valign + ';';
8910             }
8911             
8912             if(typeof(config.width) != 'undefined'){
8913                 td.style += ' width:' +  config.width + 'px;';
8914             }
8915             
8916             if(typeof(config.cursor) != 'undefined'){
8917                 td.style += ' cursor:' +  config.cursor + ';';
8918             }
8919             
8920             if(typeof(config.cls) != 'undefined'){
8921                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8922             }
8923             
8924             ['xs','sm','md','lg'].map(function(size){
8925                 
8926                 if(typeof(config[size]) == 'undefined'){
8927                     return;
8928                 }
8929                 
8930                 
8931                   
8932                 if (!config[size]) { // 0 = hidden
8933                     // BS 4 '0' is treated as hide that column and below.
8934                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8935                     return;
8936                 }
8937                 
8938                 td.cls += ' col-' + size + '-' + config[size] + (
8939                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8940                 );
8941                  
8942
8943             });
8944             
8945             row.cn.push(td);
8946            
8947         }
8948         
8949         row.cellObjects = cellObjects;
8950         
8951         return row;
8952           
8953     },
8954     
8955     
8956     
8957     onBeforeLoad : function()
8958     {
8959         
8960     },
8961      /**
8962      * Remove all rows
8963      */
8964     clear : function()
8965     {
8966         this.el.select('tbody', true).first().dom.innerHTML = '';
8967     },
8968     /**
8969      * Show or hide a row.
8970      * @param {Number} rowIndex to show or hide
8971      * @param {Boolean} state hide
8972      */
8973     setRowVisibility : function(rowIndex, state)
8974     {
8975         var bt = this.mainBody.dom;
8976         
8977         var rows = this.el.select('tbody > tr', true).elements;
8978         
8979         if(typeof(rows[rowIndex]) == 'undefined'){
8980             return;
8981         }
8982         rows[rowIndex].dom.style.display = state ? '' : 'none';
8983     },
8984     
8985     
8986     getSelectionModel : function(){
8987         if(!this.selModel){
8988             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8989         }
8990         return this.selModel;
8991     },
8992     /*
8993      * Render the Roo.bootstrap object from renderder
8994      */
8995     renderCellObject : function(r)
8996     {
8997         var _this = this;
8998         
8999         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9000         
9001         var t = r.cfg.render(r.container);
9002         
9003         if(r.cfg.cn){
9004             Roo.each(r.cfg.cn, function(c){
9005                 var child = {
9006                     container: t.getChildContainer(),
9007                     cfg: c
9008                 };
9009                 _this.renderCellObject(child);
9010             })
9011         }
9012     },
9013     
9014     getRowIndex : function(row)
9015     {
9016         var rowIndex = -1;
9017         
9018         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9019             if(el != row){
9020                 return;
9021             }
9022             
9023             rowIndex = index;
9024         });
9025         
9026         return rowIndex;
9027     },
9028      /**
9029      * Returns the grid's underlying element = used by panel.Grid
9030      * @return {Element} The element
9031      */
9032     getGridEl : function(){
9033         return this.el;
9034     },
9035      /**
9036      * Forces a resize - used by panel.Grid
9037      * @return {Element} The element
9038      */
9039     autoSize : function()
9040     {
9041         //var ctr = Roo.get(this.container.dom.parentElement);
9042         var ctr = Roo.get(this.el.dom);
9043         
9044         var thd = this.getGridEl().select('thead',true).first();
9045         var tbd = this.getGridEl().select('tbody', true).first();
9046         var tfd = this.getGridEl().select('tfoot', true).first();
9047         
9048         var cw = ctr.getWidth();
9049         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9050         
9051         if (tbd) {
9052             
9053             tbd.setWidth(ctr.getWidth());
9054             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9055             // this needs fixing for various usage - currently only hydra job advers I think..
9056             //tdb.setHeight(
9057             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9058             //); 
9059             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9060             cw -= barsize;
9061         }
9062         cw = Math.max(cw, this.totalWidth);
9063         this.getGridEl().select('tbody tr',true).setWidth(cw);
9064         
9065         // resize 'expandable coloumn?
9066         
9067         return; // we doe not have a view in this design..
9068         
9069     },
9070     onBodyScroll: function()
9071     {
9072         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9073         if(this.mainHead){
9074             this.mainHead.setStyle({
9075                 'position' : 'relative',
9076                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9077             });
9078         }
9079         
9080         if(this.lazyLoad){
9081             
9082             var scrollHeight = this.mainBody.dom.scrollHeight;
9083             
9084             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9085             
9086             var height = this.mainBody.getHeight();
9087             
9088             if(scrollHeight - height == scrollTop) {
9089                 
9090                 var total = this.ds.getTotalCount();
9091                 
9092                 if(this.footer.cursor + this.footer.pageSize < total){
9093                     
9094                     this.footer.ds.load({
9095                         params : {
9096                             start : this.footer.cursor + this.footer.pageSize,
9097                             limit : this.footer.pageSize
9098                         },
9099                         add : true
9100                     });
9101                 }
9102             }
9103             
9104         }
9105     },
9106     
9107     onHeaderChange : function()
9108     {
9109         var header = this.renderHeader();
9110         var table = this.el.select('table', true).first();
9111         
9112         this.mainHead.remove();
9113         this.mainHead = table.createChild(header, this.mainBody, false);
9114         
9115         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9116             e.on('click', this.sort, this);
9117         }, this);
9118         
9119         
9120     },
9121     
9122     onHiddenChange : function(colModel, colIndex, hidden)
9123     {
9124         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9125         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9126         
9127         this.CSS.updateRule(thSelector, "display", "");
9128         this.CSS.updateRule(tdSelector, "display", "");
9129         
9130         if(hidden){
9131             this.CSS.updateRule(thSelector, "display", "none");
9132             this.CSS.updateRule(tdSelector, "display", "none");
9133         }
9134         
9135         this.onHeaderChange();
9136         this.onLoad();
9137     },
9138     
9139     setColumnWidth: function(col_index, width)
9140     {
9141         // width = "md-2 xs-2..."
9142         if(!this.colModel.config[col_index]) {
9143             return;
9144         }
9145         
9146         var w = width.split(" ");
9147         
9148         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9149         
9150         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9151         
9152         
9153         for(var j = 0; j < w.length; j++) {
9154             
9155             if(!w[j]) {
9156                 continue;
9157             }
9158             
9159             var size_cls = w[j].split("-");
9160             
9161             if(!Number.isInteger(size_cls[1] * 1)) {
9162                 continue;
9163             }
9164             
9165             if(!this.colModel.config[col_index][size_cls[0]]) {
9166                 continue;
9167             }
9168             
9169             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9170                 continue;
9171             }
9172             
9173             h_row[0].classList.replace(
9174                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9175                 "col-"+size_cls[0]+"-"+size_cls[1]
9176             );
9177             
9178             for(var i = 0; i < rows.length; i++) {
9179                 
9180                 var size_cls = w[j].split("-");
9181                 
9182                 if(!Number.isInteger(size_cls[1] * 1)) {
9183                     continue;
9184                 }
9185                 
9186                 if(!this.colModel.config[col_index][size_cls[0]]) {
9187                     continue;
9188                 }
9189                 
9190                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9191                     continue;
9192                 }
9193                 
9194                 rows[i].classList.replace(
9195                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9196                     "col-"+size_cls[0]+"-"+size_cls[1]
9197                 );
9198             }
9199             
9200             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9201         }
9202     }
9203 });
9204
9205  
9206
9207  /*
9208  * - LGPL
9209  *
9210  * table cell
9211  * 
9212  */
9213
9214 /**
9215  * @class Roo.bootstrap.TableCell
9216  * @extends Roo.bootstrap.Component
9217  * Bootstrap TableCell class
9218  * @cfg {String} html cell contain text
9219  * @cfg {String} cls cell class
9220  * @cfg {String} tag cell tag (td|th) default td
9221  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9222  * @cfg {String} align Aligns the content in a cell
9223  * @cfg {String} axis Categorizes cells
9224  * @cfg {String} bgcolor Specifies the background color of a cell
9225  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9226  * @cfg {Number} colspan Specifies the number of columns a cell should span
9227  * @cfg {String} headers Specifies one or more header cells a cell is related to
9228  * @cfg {Number} height Sets the height of a cell
9229  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9230  * @cfg {Number} rowspan Sets the number of rows a cell should span
9231  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9232  * @cfg {String} valign Vertical aligns the content in a cell
9233  * @cfg {Number} width Specifies the width of a cell
9234  * 
9235  * @constructor
9236  * Create a new TableCell
9237  * @param {Object} config The config object
9238  */
9239
9240 Roo.bootstrap.TableCell = function(config){
9241     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9242 };
9243
9244 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9245     
9246     html: false,
9247     cls: false,
9248     tag: false,
9249     abbr: false,
9250     align: false,
9251     axis: false,
9252     bgcolor: false,
9253     charoff: false,
9254     colspan: false,
9255     headers: false,
9256     height: false,
9257     nowrap: false,
9258     rowspan: false,
9259     scope: false,
9260     valign: false,
9261     width: false,
9262     
9263     
9264     getAutoCreate : function(){
9265         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9266         
9267         cfg = {
9268             tag: 'td'
9269         };
9270         
9271         if(this.tag){
9272             cfg.tag = this.tag;
9273         }
9274         
9275         if (this.html) {
9276             cfg.html=this.html
9277         }
9278         if (this.cls) {
9279             cfg.cls=this.cls
9280         }
9281         if (this.abbr) {
9282             cfg.abbr=this.abbr
9283         }
9284         if (this.align) {
9285             cfg.align=this.align
9286         }
9287         if (this.axis) {
9288             cfg.axis=this.axis
9289         }
9290         if (this.bgcolor) {
9291             cfg.bgcolor=this.bgcolor
9292         }
9293         if (this.charoff) {
9294             cfg.charoff=this.charoff
9295         }
9296         if (this.colspan) {
9297             cfg.colspan=this.colspan
9298         }
9299         if (this.headers) {
9300             cfg.headers=this.headers
9301         }
9302         if (this.height) {
9303             cfg.height=this.height
9304         }
9305         if (this.nowrap) {
9306             cfg.nowrap=this.nowrap
9307         }
9308         if (this.rowspan) {
9309             cfg.rowspan=this.rowspan
9310         }
9311         if (this.scope) {
9312             cfg.scope=this.scope
9313         }
9314         if (this.valign) {
9315             cfg.valign=this.valign
9316         }
9317         if (this.width) {
9318             cfg.width=this.width
9319         }
9320         
9321         
9322         return cfg;
9323     }
9324    
9325 });
9326
9327  
9328
9329  /*
9330  * - LGPL
9331  *
9332  * table row
9333  * 
9334  */
9335
9336 /**
9337  * @class Roo.bootstrap.TableRow
9338  * @extends Roo.bootstrap.Component
9339  * Bootstrap TableRow class
9340  * @cfg {String} cls row class
9341  * @cfg {String} align Aligns the content in a table row
9342  * @cfg {String} bgcolor Specifies a background color for a table row
9343  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9344  * @cfg {String} valign Vertical aligns the content in a table row
9345  * 
9346  * @constructor
9347  * Create a new TableRow
9348  * @param {Object} config The config object
9349  */
9350
9351 Roo.bootstrap.TableRow = function(config){
9352     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9353 };
9354
9355 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9356     
9357     cls: false,
9358     align: false,
9359     bgcolor: false,
9360     charoff: false,
9361     valign: false,
9362     
9363     getAutoCreate : function(){
9364         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9365         
9366         cfg = {
9367             tag: 'tr'
9368         };
9369             
9370         if(this.cls){
9371             cfg.cls = this.cls;
9372         }
9373         if(this.align){
9374             cfg.align = this.align;
9375         }
9376         if(this.bgcolor){
9377             cfg.bgcolor = this.bgcolor;
9378         }
9379         if(this.charoff){
9380             cfg.charoff = this.charoff;
9381         }
9382         if(this.valign){
9383             cfg.valign = this.valign;
9384         }
9385         
9386         return cfg;
9387     }
9388    
9389 });
9390
9391  
9392
9393  /*
9394  * - LGPL
9395  *
9396  * table body
9397  * 
9398  */
9399
9400 /**
9401  * @class Roo.bootstrap.TableBody
9402  * @extends Roo.bootstrap.Component
9403  * Bootstrap TableBody class
9404  * @cfg {String} cls element class
9405  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9406  * @cfg {String} align Aligns the content inside the element
9407  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9408  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9409  * 
9410  * @constructor
9411  * Create a new TableBody
9412  * @param {Object} config The config object
9413  */
9414
9415 Roo.bootstrap.TableBody = function(config){
9416     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9417 };
9418
9419 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9420     
9421     cls: false,
9422     tag: false,
9423     align: false,
9424     charoff: false,
9425     valign: false,
9426     
9427     getAutoCreate : function(){
9428         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9429         
9430         cfg = {
9431             tag: 'tbody'
9432         };
9433             
9434         if (this.cls) {
9435             cfg.cls=this.cls
9436         }
9437         if(this.tag){
9438             cfg.tag = this.tag;
9439         }
9440         
9441         if(this.align){
9442             cfg.align = this.align;
9443         }
9444         if(this.charoff){
9445             cfg.charoff = this.charoff;
9446         }
9447         if(this.valign){
9448             cfg.valign = this.valign;
9449         }
9450         
9451         return cfg;
9452     }
9453     
9454     
9455 //    initEvents : function()
9456 //    {
9457 //        
9458 //        if(!this.store){
9459 //            return;
9460 //        }
9461 //        
9462 //        this.store = Roo.factory(this.store, Roo.data);
9463 //        this.store.on('load', this.onLoad, this);
9464 //        
9465 //        this.store.load();
9466 //        
9467 //    },
9468 //    
9469 //    onLoad: function () 
9470 //    {   
9471 //        this.fireEvent('load', this);
9472 //    }
9473 //    
9474 //   
9475 });
9476
9477  
9478
9479  /*
9480  * Based on:
9481  * Ext JS Library 1.1.1
9482  * Copyright(c) 2006-2007, Ext JS, LLC.
9483  *
9484  * Originally Released Under LGPL - original licence link has changed is not relivant.
9485  *
9486  * Fork - LGPL
9487  * <script type="text/javascript">
9488  */
9489
9490 // as we use this in bootstrap.
9491 Roo.namespace('Roo.form');
9492  /**
9493  * @class Roo.form.Action
9494  * Internal Class used to handle form actions
9495  * @constructor
9496  * @param {Roo.form.BasicForm} el The form element or its id
9497  * @param {Object} config Configuration options
9498  */
9499
9500  
9501  
9502 // define the action interface
9503 Roo.form.Action = function(form, options){
9504     this.form = form;
9505     this.options = options || {};
9506 };
9507 /**
9508  * Client Validation Failed
9509  * @const 
9510  */
9511 Roo.form.Action.CLIENT_INVALID = 'client';
9512 /**
9513  * Server Validation Failed
9514  * @const 
9515  */
9516 Roo.form.Action.SERVER_INVALID = 'server';
9517  /**
9518  * Connect to Server Failed
9519  * @const 
9520  */
9521 Roo.form.Action.CONNECT_FAILURE = 'connect';
9522 /**
9523  * Reading Data from Server Failed
9524  * @const 
9525  */
9526 Roo.form.Action.LOAD_FAILURE = 'load';
9527
9528 Roo.form.Action.prototype = {
9529     type : 'default',
9530     failureType : undefined,
9531     response : undefined,
9532     result : undefined,
9533
9534     // interface method
9535     run : function(options){
9536
9537     },
9538
9539     // interface method
9540     success : function(response){
9541
9542     },
9543
9544     // interface method
9545     handleResponse : function(response){
9546
9547     },
9548
9549     // default connection failure
9550     failure : function(response){
9551         
9552         this.response = response;
9553         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9554         this.form.afterAction(this, false);
9555     },
9556
9557     processResponse : function(response){
9558         this.response = response;
9559         if(!response.responseText){
9560             return true;
9561         }
9562         this.result = this.handleResponse(response);
9563         return this.result;
9564     },
9565
9566     // utility functions used internally
9567     getUrl : function(appendParams){
9568         var url = this.options.url || this.form.url || this.form.el.dom.action;
9569         if(appendParams){
9570             var p = this.getParams();
9571             if(p){
9572                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9573             }
9574         }
9575         return url;
9576     },
9577
9578     getMethod : function(){
9579         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9580     },
9581
9582     getParams : function(){
9583         var bp = this.form.baseParams;
9584         var p = this.options.params;
9585         if(p){
9586             if(typeof p == "object"){
9587                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9588             }else if(typeof p == 'string' && bp){
9589                 p += '&' + Roo.urlEncode(bp);
9590             }
9591         }else if(bp){
9592             p = Roo.urlEncode(bp);
9593         }
9594         return p;
9595     },
9596
9597     createCallback : function(){
9598         return {
9599             success: this.success,
9600             failure: this.failure,
9601             scope: this,
9602             timeout: (this.form.timeout*1000),
9603             upload: this.form.fileUpload ? this.success : undefined
9604         };
9605     }
9606 };
9607
9608 Roo.form.Action.Submit = function(form, options){
9609     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9610 };
9611
9612 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9613     type : 'submit',
9614
9615     haveProgress : false,
9616     uploadComplete : false,
9617     
9618     // uploadProgress indicator.
9619     uploadProgress : function()
9620     {
9621         if (!this.form.progressUrl) {
9622             return;
9623         }
9624         
9625         if (!this.haveProgress) {
9626             Roo.MessageBox.progress("Uploading", "Uploading");
9627         }
9628         if (this.uploadComplete) {
9629            Roo.MessageBox.hide();
9630            return;
9631         }
9632         
9633         this.haveProgress = true;
9634    
9635         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9636         
9637         var c = new Roo.data.Connection();
9638         c.request({
9639             url : this.form.progressUrl,
9640             params: {
9641                 id : uid
9642             },
9643             method: 'GET',
9644             success : function(req){
9645                //console.log(data);
9646                 var rdata = false;
9647                 var edata;
9648                 try  {
9649                    rdata = Roo.decode(req.responseText)
9650                 } catch (e) {
9651                     Roo.log("Invalid data from server..");
9652                     Roo.log(edata);
9653                     return;
9654                 }
9655                 if (!rdata || !rdata.success) {
9656                     Roo.log(rdata);
9657                     Roo.MessageBox.alert(Roo.encode(rdata));
9658                     return;
9659                 }
9660                 var data = rdata.data;
9661                 
9662                 if (this.uploadComplete) {
9663                    Roo.MessageBox.hide();
9664                    return;
9665                 }
9666                    
9667                 if (data){
9668                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9669                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9670                     );
9671                 }
9672                 this.uploadProgress.defer(2000,this);
9673             },
9674        
9675             failure: function(data) {
9676                 Roo.log('progress url failed ');
9677                 Roo.log(data);
9678             },
9679             scope : this
9680         });
9681            
9682     },
9683     
9684     
9685     run : function()
9686     {
9687         // run get Values on the form, so it syncs any secondary forms.
9688         this.form.getValues();
9689         
9690         var o = this.options;
9691         var method = this.getMethod();
9692         var isPost = method == 'POST';
9693         if(o.clientValidation === false || this.form.isValid()){
9694             
9695             if (this.form.progressUrl) {
9696                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9697                     (new Date() * 1) + '' + Math.random());
9698                     
9699             } 
9700             
9701             
9702             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9703                 form:this.form.el.dom,
9704                 url:this.getUrl(!isPost),
9705                 method: method,
9706                 params:isPost ? this.getParams() : null,
9707                 isUpload: this.form.fileUpload,
9708                 formData : this.form.formData
9709             }));
9710             
9711             this.uploadProgress();
9712
9713         }else if (o.clientValidation !== false){ // client validation failed
9714             this.failureType = Roo.form.Action.CLIENT_INVALID;
9715             this.form.afterAction(this, false);
9716         }
9717     },
9718
9719     success : function(response)
9720     {
9721         this.uploadComplete= true;
9722         if (this.haveProgress) {
9723             Roo.MessageBox.hide();
9724         }
9725         
9726         
9727         var result = this.processResponse(response);
9728         if(result === true || result.success){
9729             this.form.afterAction(this, true);
9730             return;
9731         }
9732         if(result.errors){
9733             this.form.markInvalid(result.errors);
9734             this.failureType = Roo.form.Action.SERVER_INVALID;
9735         }
9736         this.form.afterAction(this, false);
9737     },
9738     failure : function(response)
9739     {
9740         this.uploadComplete= true;
9741         if (this.haveProgress) {
9742             Roo.MessageBox.hide();
9743         }
9744         
9745         this.response = response;
9746         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9747         this.form.afterAction(this, false);
9748     },
9749     
9750     handleResponse : function(response){
9751         if(this.form.errorReader){
9752             var rs = this.form.errorReader.read(response);
9753             var errors = [];
9754             if(rs.records){
9755                 for(var i = 0, len = rs.records.length; i < len; i++) {
9756                     var r = rs.records[i];
9757                     errors[i] = r.data;
9758                 }
9759             }
9760             if(errors.length < 1){
9761                 errors = null;
9762             }
9763             return {
9764                 success : rs.success,
9765                 errors : errors
9766             };
9767         }
9768         var ret = false;
9769         try {
9770             ret = Roo.decode(response.responseText);
9771         } catch (e) {
9772             ret = {
9773                 success: false,
9774                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9775                 errors : []
9776             };
9777         }
9778         return ret;
9779         
9780     }
9781 });
9782
9783
9784 Roo.form.Action.Load = function(form, options){
9785     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9786     this.reader = this.form.reader;
9787 };
9788
9789 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9790     type : 'load',
9791
9792     run : function(){
9793         
9794         Roo.Ajax.request(Roo.apply(
9795                 this.createCallback(), {
9796                     method:this.getMethod(),
9797                     url:this.getUrl(false),
9798                     params:this.getParams()
9799         }));
9800     },
9801
9802     success : function(response){
9803         
9804         var result = this.processResponse(response);
9805         if(result === true || !result.success || !result.data){
9806             this.failureType = Roo.form.Action.LOAD_FAILURE;
9807             this.form.afterAction(this, false);
9808             return;
9809         }
9810         this.form.clearInvalid();
9811         this.form.setValues(result.data);
9812         this.form.afterAction(this, true);
9813     },
9814
9815     handleResponse : function(response){
9816         if(this.form.reader){
9817             var rs = this.form.reader.read(response);
9818             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9819             return {
9820                 success : rs.success,
9821                 data : data
9822             };
9823         }
9824         return Roo.decode(response.responseText);
9825     }
9826 });
9827
9828 Roo.form.Action.ACTION_TYPES = {
9829     'load' : Roo.form.Action.Load,
9830     'submit' : Roo.form.Action.Submit
9831 };/*
9832  * - LGPL
9833  *
9834  * form
9835  *
9836  */
9837
9838 /**
9839  * @class Roo.bootstrap.Form
9840  * @extends Roo.bootstrap.Component
9841  * Bootstrap Form class
9842  * @cfg {String} method  GET | POST (default POST)
9843  * @cfg {String} labelAlign top | left (default top)
9844  * @cfg {String} align left  | right - for navbars
9845  * @cfg {Boolean} loadMask load mask when submit (default true)
9846
9847  *
9848  * @constructor
9849  * Create a new Form
9850  * @param {Object} config The config object
9851  */
9852
9853
9854 Roo.bootstrap.Form = function(config){
9855     
9856     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9857     
9858     Roo.bootstrap.Form.popover.apply();
9859     
9860     this.addEvents({
9861         /**
9862          * @event clientvalidation
9863          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9864          * @param {Form} this
9865          * @param {Boolean} valid true if the form has passed client-side validation
9866          */
9867         clientvalidation: true,
9868         /**
9869          * @event beforeaction
9870          * Fires before any action is performed. Return false to cancel the action.
9871          * @param {Form} this
9872          * @param {Action} action The action to be performed
9873          */
9874         beforeaction: true,
9875         /**
9876          * @event actionfailed
9877          * Fires when an action fails.
9878          * @param {Form} this
9879          * @param {Action} action The action that failed
9880          */
9881         actionfailed : true,
9882         /**
9883          * @event actioncomplete
9884          * Fires when an action is completed.
9885          * @param {Form} this
9886          * @param {Action} action The action that completed
9887          */
9888         actioncomplete : true
9889     });
9890 };
9891
9892 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9893
9894      /**
9895      * @cfg {String} method
9896      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9897      */
9898     method : 'POST',
9899     /**
9900      * @cfg {String} url
9901      * The URL to use for form actions if one isn't supplied in the action options.
9902      */
9903     /**
9904      * @cfg {Boolean} fileUpload
9905      * Set to true if this form is a file upload.
9906      */
9907
9908     /**
9909      * @cfg {Object} baseParams
9910      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9911      */
9912
9913     /**
9914      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9915      */
9916     timeout: 30,
9917     /**
9918      * @cfg {Sting} align (left|right) for navbar forms
9919      */
9920     align : 'left',
9921
9922     // private
9923     activeAction : null,
9924
9925     /**
9926      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9927      * element by passing it or its id or mask the form itself by passing in true.
9928      * @type Mixed
9929      */
9930     waitMsgTarget : false,
9931
9932     loadMask : true,
9933     
9934     /**
9935      * @cfg {Boolean} errorMask (true|false) default false
9936      */
9937     errorMask : false,
9938     
9939     /**
9940      * @cfg {Number} maskOffset Default 100
9941      */
9942     maskOffset : 100,
9943     
9944     /**
9945      * @cfg {Boolean} maskBody
9946      */
9947     maskBody : false,
9948
9949     getAutoCreate : function(){
9950
9951         var cfg = {
9952             tag: 'form',
9953             method : this.method || 'POST',
9954             id : this.id || Roo.id(),
9955             cls : ''
9956         };
9957         if (this.parent().xtype.match(/^Nav/)) {
9958             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9959
9960         }
9961
9962         if (this.labelAlign == 'left' ) {
9963             cfg.cls += ' form-horizontal';
9964         }
9965
9966
9967         return cfg;
9968     },
9969     initEvents : function()
9970     {
9971         this.el.on('submit', this.onSubmit, this);
9972         // this was added as random key presses on the form where triggering form submit.
9973         this.el.on('keypress', function(e) {
9974             if (e.getCharCode() != 13) {
9975                 return true;
9976             }
9977             // we might need to allow it for textareas.. and some other items.
9978             // check e.getTarget().
9979
9980             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9981                 return true;
9982             }
9983
9984             Roo.log("keypress blocked");
9985
9986             e.preventDefault();
9987             return false;
9988         });
9989         
9990     },
9991     // private
9992     onSubmit : function(e){
9993         e.stopEvent();
9994     },
9995
9996      /**
9997      * Returns true if client-side validation on the form is successful.
9998      * @return Boolean
9999      */
10000     isValid : function(){
10001         var items = this.getItems();
10002         var valid = true;
10003         var target = false;
10004         
10005         items.each(function(f){
10006             
10007             if(f.validate()){
10008                 return;
10009             }
10010             
10011             Roo.log('invalid field: ' + f.name);
10012             
10013             valid = false;
10014
10015             if(!target && f.el.isVisible(true)){
10016                 target = f;
10017             }
10018            
10019         });
10020         
10021         if(this.errorMask && !valid){
10022             Roo.bootstrap.Form.popover.mask(this, target);
10023         }
10024         
10025         return valid;
10026     },
10027     
10028     /**
10029      * Returns true if any fields in this form have changed since their original load.
10030      * @return Boolean
10031      */
10032     isDirty : function(){
10033         var dirty = false;
10034         var items = this.getItems();
10035         items.each(function(f){
10036            if(f.isDirty()){
10037                dirty = true;
10038                return false;
10039            }
10040            return true;
10041         });
10042         return dirty;
10043     },
10044      /**
10045      * Performs a predefined action (submit or load) or custom actions you define on this form.
10046      * @param {String} actionName The name of the action type
10047      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10048      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10049      * accept other config options):
10050      * <pre>
10051 Property          Type             Description
10052 ----------------  ---------------  ----------------------------------------------------------------------------------
10053 url               String           The url for the action (defaults to the form's url)
10054 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10055 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10056 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10057                                    validate the form on the client (defaults to false)
10058      * </pre>
10059      * @return {BasicForm} this
10060      */
10061     doAction : function(action, options){
10062         if(typeof action == 'string'){
10063             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10064         }
10065         if(this.fireEvent('beforeaction', this, action) !== false){
10066             this.beforeAction(action);
10067             action.run.defer(100, action);
10068         }
10069         return this;
10070     },
10071
10072     // private
10073     beforeAction : function(action){
10074         var o = action.options;
10075         
10076         if(this.loadMask){
10077             
10078             if(this.maskBody){
10079                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10080             } else {
10081                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10082             }
10083         }
10084         // not really supported yet.. ??
10085
10086         //if(this.waitMsgTarget === true){
10087         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088         //}else if(this.waitMsgTarget){
10089         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10090         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10091         //}else {
10092         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10093        // }
10094
10095     },
10096
10097     // private
10098     afterAction : function(action, success){
10099         this.activeAction = null;
10100         var o = action.options;
10101
10102         if(this.loadMask){
10103             
10104             if(this.maskBody){
10105                 Roo.get(document.body).unmask();
10106             } else {
10107                 this.el.unmask();
10108             }
10109         }
10110         
10111         //if(this.waitMsgTarget === true){
10112 //            this.el.unmask();
10113         //}else if(this.waitMsgTarget){
10114         //    this.waitMsgTarget.unmask();
10115         //}else{
10116         //    Roo.MessageBox.updateProgress(1);
10117         //    Roo.MessageBox.hide();
10118        // }
10119         //
10120         if(success){
10121             if(o.reset){
10122                 this.reset();
10123             }
10124             Roo.callback(o.success, o.scope, [this, action]);
10125             this.fireEvent('actioncomplete', this, action);
10126
10127         }else{
10128
10129             // failure condition..
10130             // we have a scenario where updates need confirming.
10131             // eg. if a locking scenario exists..
10132             // we look for { errors : { needs_confirm : true }} in the response.
10133             if (
10134                 (typeof(action.result) != 'undefined')  &&
10135                 (typeof(action.result.errors) != 'undefined')  &&
10136                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10137            ){
10138                 var _t = this;
10139                 Roo.log("not supported yet");
10140                  /*
10141
10142                 Roo.MessageBox.confirm(
10143                     "Change requires confirmation",
10144                     action.result.errorMsg,
10145                     function(r) {
10146                         if (r != 'yes') {
10147                             return;
10148                         }
10149                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10150                     }
10151
10152                 );
10153                 */
10154
10155
10156                 return;
10157             }
10158
10159             Roo.callback(o.failure, o.scope, [this, action]);
10160             // show an error message if no failed handler is set..
10161             if (!this.hasListener('actionfailed')) {
10162                 Roo.log("need to add dialog support");
10163                 /*
10164                 Roo.MessageBox.alert("Error",
10165                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10166                         action.result.errorMsg :
10167                         "Saving Failed, please check your entries or try again"
10168                 );
10169                 */
10170             }
10171
10172             this.fireEvent('actionfailed', this, action);
10173         }
10174
10175     },
10176     /**
10177      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10178      * @param {String} id The value to search for
10179      * @return Field
10180      */
10181     findField : function(id){
10182         var items = this.getItems();
10183         var field = items.get(id);
10184         if(!field){
10185              items.each(function(f){
10186                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10187                     field = f;
10188                     return false;
10189                 }
10190                 return true;
10191             });
10192         }
10193         return field || null;
10194     },
10195      /**
10196      * Mark fields in this form invalid in bulk.
10197      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10198      * @return {BasicForm} this
10199      */
10200     markInvalid : function(errors){
10201         if(errors instanceof Array){
10202             for(var i = 0, len = errors.length; i < len; i++){
10203                 var fieldError = errors[i];
10204                 var f = this.findField(fieldError.id);
10205                 if(f){
10206                     f.markInvalid(fieldError.msg);
10207                 }
10208             }
10209         }else{
10210             var field, id;
10211             for(id in errors){
10212                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10213                     field.markInvalid(errors[id]);
10214                 }
10215             }
10216         }
10217         //Roo.each(this.childForms || [], function (f) {
10218         //    f.markInvalid(errors);
10219         //});
10220
10221         return this;
10222     },
10223
10224     /**
10225      * Set values for fields in this form in bulk.
10226      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10227      * @return {BasicForm} this
10228      */
10229     setValues : function(values){
10230         if(values instanceof Array){ // array of objects
10231             for(var i = 0, len = values.length; i < len; i++){
10232                 var v = values[i];
10233                 var f = this.findField(v.id);
10234                 if(f){
10235                     f.setValue(v.value);
10236                     if(this.trackResetOnLoad){
10237                         f.originalValue = f.getValue();
10238                     }
10239                 }
10240             }
10241         }else{ // object hash
10242             var field, id;
10243             for(id in values){
10244                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10245
10246                     if (field.setFromData &&
10247                         field.valueField &&
10248                         field.displayField &&
10249                         // combos' with local stores can
10250                         // be queried via setValue()
10251                         // to set their value..
10252                         (field.store && !field.store.isLocal)
10253                         ) {
10254                         // it's a combo
10255                         var sd = { };
10256                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10257                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10258                         field.setFromData(sd);
10259
10260                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10261                         
10262                         field.setFromData(values);
10263                         
10264                     } else {
10265                         field.setValue(values[id]);
10266                     }
10267
10268
10269                     if(this.trackResetOnLoad){
10270                         field.originalValue = field.getValue();
10271                     }
10272                 }
10273             }
10274         }
10275
10276         //Roo.each(this.childForms || [], function (f) {
10277         //    f.setValues(values);
10278         //});
10279
10280         return this;
10281     },
10282
10283     /**
10284      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10285      * they are returned as an array.
10286      * @param {Boolean} asString
10287      * @return {Object}
10288      */
10289     getValues : function(asString){
10290         //if (this.childForms) {
10291             // copy values from the child forms
10292         //    Roo.each(this.childForms, function (f) {
10293         //        this.setValues(f.getValues());
10294         //    }, this);
10295         //}
10296
10297
10298
10299         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10300         if(asString === true){
10301             return fs;
10302         }
10303         return Roo.urlDecode(fs);
10304     },
10305
10306     /**
10307      * Returns the fields in this form as an object with key/value pairs.
10308      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10309      * @return {Object}
10310      */
10311     getFieldValues : function(with_hidden)
10312     {
10313         var items = this.getItems();
10314         var ret = {};
10315         items.each(function(f){
10316             
10317             if (!f.getName()) {
10318                 return;
10319             }
10320             
10321             var v = f.getValue();
10322             
10323             if (f.inputType =='radio') {
10324                 if (typeof(ret[f.getName()]) == 'undefined') {
10325                     ret[f.getName()] = ''; // empty..
10326                 }
10327
10328                 if (!f.el.dom.checked) {
10329                     return;
10330
10331                 }
10332                 v = f.el.dom.value;
10333
10334             }
10335             
10336             if(f.xtype == 'MoneyField'){
10337                 ret[f.currencyName] = f.getCurrency();
10338             }
10339
10340             // not sure if this supported any more..
10341             if ((typeof(v) == 'object') && f.getRawValue) {
10342                 v = f.getRawValue() ; // dates..
10343             }
10344             // combo boxes where name != hiddenName...
10345             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10346                 ret[f.name] = f.getRawValue();
10347             }
10348             ret[f.getName()] = v;
10349         });
10350
10351         return ret;
10352     },
10353
10354     /**
10355      * Clears all invalid messages in this form.
10356      * @return {BasicForm} this
10357      */
10358     clearInvalid : function(){
10359         var items = this.getItems();
10360
10361         items.each(function(f){
10362            f.clearInvalid();
10363         });
10364
10365         return this;
10366     },
10367
10368     /**
10369      * Resets this form.
10370      * @return {BasicForm} this
10371      */
10372     reset : function(){
10373         var items = this.getItems();
10374         items.each(function(f){
10375             f.reset();
10376         });
10377
10378         Roo.each(this.childForms || [], function (f) {
10379             f.reset();
10380         });
10381
10382
10383         return this;
10384     },
10385     
10386     getItems : function()
10387     {
10388         var r=new Roo.util.MixedCollection(false, function(o){
10389             return o.id || (o.id = Roo.id());
10390         });
10391         var iter = function(el) {
10392             if (el.inputEl) {
10393                 r.add(el);
10394             }
10395             if (!el.items) {
10396                 return;
10397             }
10398             Roo.each(el.items,function(e) {
10399                 iter(e);
10400             });
10401         };
10402
10403         iter(this);
10404         return r;
10405     },
10406     
10407     hideFields : function(items)
10408     {
10409         Roo.each(items, function(i){
10410             
10411             var f = this.findField(i);
10412             
10413             if(!f){
10414                 return;
10415             }
10416             
10417             f.hide();
10418             
10419         }, this);
10420     },
10421     
10422     showFields : function(items)
10423     {
10424         Roo.each(items, function(i){
10425             
10426             var f = this.findField(i);
10427             
10428             if(!f){
10429                 return;
10430             }
10431             
10432             f.show();
10433             
10434         }, this);
10435     }
10436
10437 });
10438
10439 Roo.apply(Roo.bootstrap.Form, {
10440     
10441     popover : {
10442         
10443         padding : 5,
10444         
10445         isApplied : false,
10446         
10447         isMasked : false,
10448         
10449         form : false,
10450         
10451         target : false,
10452         
10453         toolTip : false,
10454         
10455         intervalID : false,
10456         
10457         maskEl : false,
10458         
10459         apply : function()
10460         {
10461             if(this.isApplied){
10462                 return;
10463             }
10464             
10465             this.maskEl = {
10466                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10467                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10468                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10469                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10470             };
10471             
10472             this.maskEl.top.enableDisplayMode("block");
10473             this.maskEl.left.enableDisplayMode("block");
10474             this.maskEl.bottom.enableDisplayMode("block");
10475             this.maskEl.right.enableDisplayMode("block");
10476             
10477             this.toolTip = new Roo.bootstrap.Tooltip({
10478                 cls : 'roo-form-error-popover',
10479                 alignment : {
10480                     'left' : ['r-l', [-2,0], 'right'],
10481                     'right' : ['l-r', [2,0], 'left'],
10482                     'bottom' : ['tl-bl', [0,2], 'top'],
10483                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10484                 }
10485             });
10486             
10487             this.toolTip.render(Roo.get(document.body));
10488
10489             this.toolTip.el.enableDisplayMode("block");
10490             
10491             Roo.get(document.body).on('click', function(){
10492                 this.unmask();
10493             }, this);
10494             
10495             Roo.get(document.body).on('touchstart', function(){
10496                 this.unmask();
10497             }, this);
10498             
10499             this.isApplied = true
10500         },
10501         
10502         mask : function(form, target)
10503         {
10504             this.form = form;
10505             
10506             this.target = target;
10507             
10508             if(!this.form.errorMask || !target.el){
10509                 return;
10510             }
10511             
10512             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10513             
10514             Roo.log(scrollable);
10515             
10516             var ot = this.target.el.calcOffsetsTo(scrollable);
10517             
10518             var scrollTo = ot[1] - this.form.maskOffset;
10519             
10520             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10521             
10522             scrollable.scrollTo('top', scrollTo);
10523             
10524             var box = this.target.el.getBox();
10525             Roo.log(box);
10526             var zIndex = Roo.bootstrap.Modal.zIndex++;
10527
10528             
10529             this.maskEl.top.setStyle('position', 'absolute');
10530             this.maskEl.top.setStyle('z-index', zIndex);
10531             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10532             this.maskEl.top.setLeft(0);
10533             this.maskEl.top.setTop(0);
10534             this.maskEl.top.show();
10535             
10536             this.maskEl.left.setStyle('position', 'absolute');
10537             this.maskEl.left.setStyle('z-index', zIndex);
10538             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10539             this.maskEl.left.setLeft(0);
10540             this.maskEl.left.setTop(box.y - this.padding);
10541             this.maskEl.left.show();
10542
10543             this.maskEl.bottom.setStyle('position', 'absolute');
10544             this.maskEl.bottom.setStyle('z-index', zIndex);
10545             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10546             this.maskEl.bottom.setLeft(0);
10547             this.maskEl.bottom.setTop(box.bottom + this.padding);
10548             this.maskEl.bottom.show();
10549
10550             this.maskEl.right.setStyle('position', 'absolute');
10551             this.maskEl.right.setStyle('z-index', zIndex);
10552             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10553             this.maskEl.right.setLeft(box.right + this.padding);
10554             this.maskEl.right.setTop(box.y - this.padding);
10555             this.maskEl.right.show();
10556
10557             this.toolTip.bindEl = this.target.el;
10558
10559             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10560
10561             var tip = this.target.blankText;
10562
10563             if(this.target.getValue() !== '' ) {
10564                 
10565                 if (this.target.invalidText.length) {
10566                     tip = this.target.invalidText;
10567                 } else if (this.target.regexText.length){
10568                     tip = this.target.regexText;
10569                 }
10570             }
10571
10572             this.toolTip.show(tip);
10573
10574             this.intervalID = window.setInterval(function() {
10575                 Roo.bootstrap.Form.popover.unmask();
10576             }, 10000);
10577
10578             window.onwheel = function(){ return false;};
10579             
10580             (function(){ this.isMasked = true; }).defer(500, this);
10581             
10582         },
10583         
10584         unmask : function()
10585         {
10586             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10587                 return;
10588             }
10589             
10590             this.maskEl.top.setStyle('position', 'absolute');
10591             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10592             this.maskEl.top.hide();
10593
10594             this.maskEl.left.setStyle('position', 'absolute');
10595             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10596             this.maskEl.left.hide();
10597
10598             this.maskEl.bottom.setStyle('position', 'absolute');
10599             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10600             this.maskEl.bottom.hide();
10601
10602             this.maskEl.right.setStyle('position', 'absolute');
10603             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10604             this.maskEl.right.hide();
10605             
10606             this.toolTip.hide();
10607             
10608             this.toolTip.el.hide();
10609             
10610             window.onwheel = function(){ return true;};
10611             
10612             if(this.intervalID){
10613                 window.clearInterval(this.intervalID);
10614                 this.intervalID = false;
10615             }
10616             
10617             this.isMasked = false;
10618             
10619         }
10620         
10621     }
10622     
10623 });
10624
10625 /*
10626  * Based on:
10627  * Ext JS Library 1.1.1
10628  * Copyright(c) 2006-2007, Ext JS, LLC.
10629  *
10630  * Originally Released Under LGPL - original licence link has changed is not relivant.
10631  *
10632  * Fork - LGPL
10633  * <script type="text/javascript">
10634  */
10635 /**
10636  * @class Roo.form.VTypes
10637  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10638  * @singleton
10639  */
10640 Roo.form.VTypes = function(){
10641     // closure these in so they are only created once.
10642     var alpha = /^[a-zA-Z_]+$/;
10643     var alphanum = /^[a-zA-Z0-9_]+$/;
10644     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10645     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10646
10647     // All these messages and functions are configurable
10648     return {
10649         /**
10650          * The function used to validate email addresses
10651          * @param {String} value The email address
10652          */
10653         'email' : function(v){
10654             return email.test(v);
10655         },
10656         /**
10657          * The error text to display when the email validation function returns false
10658          * @type String
10659          */
10660         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10661         /**
10662          * The keystroke filter mask to be applied on email input
10663          * @type RegExp
10664          */
10665         'emailMask' : /[a-z0-9_\.\-@]/i,
10666
10667         /**
10668          * The function used to validate URLs
10669          * @param {String} value The URL
10670          */
10671         'url' : function(v){
10672             return url.test(v);
10673         },
10674         /**
10675          * The error text to display when the url validation function returns false
10676          * @type String
10677          */
10678         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10679         
10680         /**
10681          * The function used to validate alpha values
10682          * @param {String} value The value
10683          */
10684         'alpha' : function(v){
10685             return alpha.test(v);
10686         },
10687         /**
10688          * The error text to display when the alpha validation function returns false
10689          * @type String
10690          */
10691         'alphaText' : 'This field should only contain letters and _',
10692         /**
10693          * The keystroke filter mask to be applied on alpha input
10694          * @type RegExp
10695          */
10696         'alphaMask' : /[a-z_]/i,
10697
10698         /**
10699          * The function used to validate alphanumeric values
10700          * @param {String} value The value
10701          */
10702         'alphanum' : function(v){
10703             return alphanum.test(v);
10704         },
10705         /**
10706          * The error text to display when the alphanumeric validation function returns false
10707          * @type String
10708          */
10709         'alphanumText' : 'This field should only contain letters, numbers and _',
10710         /**
10711          * The keystroke filter mask to be applied on alphanumeric input
10712          * @type RegExp
10713          */
10714         'alphanumMask' : /[a-z0-9_]/i
10715     };
10716 }();/*
10717  * - LGPL
10718  *
10719  * Input
10720  * 
10721  */
10722
10723 /**
10724  * @class Roo.bootstrap.Input
10725  * @extends Roo.bootstrap.Component
10726  * Bootstrap Input class
10727  * @cfg {Boolean} disabled is it disabled
10728  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10729  * @cfg {String} name name of the input
10730  * @cfg {string} fieldLabel - the label associated
10731  * @cfg {string} placeholder - placeholder to put in text.
10732  * @cfg {string}  before - input group add on before
10733  * @cfg {string} after - input group add on after
10734  * @cfg {string} size - (lg|sm) or leave empty..
10735  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10736  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10737  * @cfg {Number} md colspan out of 12 for computer-sized screens
10738  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10739  * @cfg {string} value default value of the input
10740  * @cfg {Number} labelWidth set the width of label 
10741  * @cfg {Number} labellg set the width of label (1-12)
10742  * @cfg {Number} labelmd set the width of label (1-12)
10743  * @cfg {Number} labelsm set the width of label (1-12)
10744  * @cfg {Number} labelxs set the width of label (1-12)
10745  * @cfg {String} labelAlign (top|left)
10746  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10747  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10748  * @cfg {String} indicatorpos (left|right) default left
10749  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10750  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10751  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10752
10753  * @cfg {String} align (left|center|right) Default left
10754  * @cfg {Boolean} forceFeedback (true|false) Default false
10755  * 
10756  * @constructor
10757  * Create a new Input
10758  * @param {Object} config The config object
10759  */
10760
10761 Roo.bootstrap.Input = function(config){
10762     
10763     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10764     
10765     this.addEvents({
10766         /**
10767          * @event focus
10768          * Fires when this field receives input focus.
10769          * @param {Roo.form.Field} this
10770          */
10771         focus : true,
10772         /**
10773          * @event blur
10774          * Fires when this field loses input focus.
10775          * @param {Roo.form.Field} this
10776          */
10777         blur : true,
10778         /**
10779          * @event specialkey
10780          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10781          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10782          * @param {Roo.form.Field} this
10783          * @param {Roo.EventObject} e The event object
10784          */
10785         specialkey : true,
10786         /**
10787          * @event change
10788          * Fires just before the field blurs if the field value has changed.
10789          * @param {Roo.form.Field} this
10790          * @param {Mixed} newValue The new value
10791          * @param {Mixed} oldValue The original value
10792          */
10793         change : true,
10794         /**
10795          * @event invalid
10796          * Fires after the field has been marked as invalid.
10797          * @param {Roo.form.Field} this
10798          * @param {String} msg The validation message
10799          */
10800         invalid : true,
10801         /**
10802          * @event valid
10803          * Fires after the field has been validated with no errors.
10804          * @param {Roo.form.Field} this
10805          */
10806         valid : true,
10807          /**
10808          * @event keyup
10809          * Fires after the key up
10810          * @param {Roo.form.Field} this
10811          * @param {Roo.EventObject}  e The event Object
10812          */
10813         keyup : true,
10814         /**
10815          * @event paste
10816          * Fires after the user pastes into input
10817          * @param {Roo.form.Field} this
10818          * @param {Roo.EventObject}  e The event Object
10819          */
10820         paste : true
10821     });
10822 };
10823
10824 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10825      /**
10826      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10827       automatic validation (defaults to "keyup").
10828      */
10829     validationEvent : "keyup",
10830      /**
10831      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10832      */
10833     validateOnBlur : true,
10834     /**
10835      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10836      */
10837     validationDelay : 250,
10838      /**
10839      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10840      */
10841     focusClass : "x-form-focus",  // not needed???
10842     
10843        
10844     /**
10845      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10846      */
10847     invalidClass : "has-warning",
10848     
10849     /**
10850      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10851      */
10852     validClass : "has-success",
10853     
10854     /**
10855      * @cfg {Boolean} hasFeedback (true|false) default true
10856      */
10857     hasFeedback : true,
10858     
10859     /**
10860      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10861      */
10862     invalidFeedbackClass : "glyphicon-warning-sign",
10863     
10864     /**
10865      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10866      */
10867     validFeedbackClass : "glyphicon-ok",
10868     
10869     /**
10870      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10871      */
10872     selectOnFocus : false,
10873     
10874      /**
10875      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10876      */
10877     maskRe : null,
10878        /**
10879      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10880      */
10881     vtype : null,
10882     
10883       /**
10884      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10885      */
10886     disableKeyFilter : false,
10887     
10888        /**
10889      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10890      */
10891     disabled : false,
10892      /**
10893      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10894      */
10895     allowBlank : true,
10896     /**
10897      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10898      */
10899     blankText : "Please complete this mandatory field",
10900     
10901      /**
10902      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10903      */
10904     minLength : 0,
10905     /**
10906      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10907      */
10908     maxLength : Number.MAX_VALUE,
10909     /**
10910      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10911      */
10912     minLengthText : "The minimum length for this field is {0}",
10913     /**
10914      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10915      */
10916     maxLengthText : "The maximum length for this field is {0}",
10917   
10918     
10919     /**
10920      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10921      * If available, this function will be called only after the basic validators all return true, and will be passed the
10922      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10923      */
10924     validator : null,
10925     /**
10926      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10927      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10928      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10929      */
10930     regex : null,
10931     /**
10932      * @cfg {String} regexText -- Depricated - use Invalid Text
10933      */
10934     regexText : "",
10935     
10936     /**
10937      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10938      */
10939     invalidText : "",
10940     
10941     
10942     
10943     autocomplete: false,
10944     
10945     
10946     fieldLabel : '',
10947     inputType : 'text',
10948     
10949     name : false,
10950     placeholder: false,
10951     before : false,
10952     after : false,
10953     size : false,
10954     hasFocus : false,
10955     preventMark: false,
10956     isFormField : true,
10957     value : '',
10958     labelWidth : 2,
10959     labelAlign : false,
10960     readOnly : false,
10961     align : false,
10962     formatedValue : false,
10963     forceFeedback : false,
10964     
10965     indicatorpos : 'left',
10966     
10967     labellg : 0,
10968     labelmd : 0,
10969     labelsm : 0,
10970     labelxs : 0,
10971     
10972     capture : '',
10973     accept : '',
10974     
10975     parentLabelAlign : function()
10976     {
10977         var parent = this;
10978         while (parent.parent()) {
10979             parent = parent.parent();
10980             if (typeof(parent.labelAlign) !='undefined') {
10981                 return parent.labelAlign;
10982             }
10983         }
10984         return 'left';
10985         
10986     },
10987     
10988     getAutoCreate : function()
10989     {
10990         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10991         
10992         var id = Roo.id();
10993         
10994         var cfg = {};
10995         
10996         if(this.inputType != 'hidden'){
10997             cfg.cls = 'form-group' //input-group
10998         }
10999         
11000         var input =  {
11001             tag: 'input',
11002             id : id,
11003             type : this.inputType,
11004             value : this.value,
11005             cls : 'form-control',
11006             placeholder : this.placeholder || '',
11007             autocomplete : this.autocomplete || 'new-password'
11008         };
11009         if (this.inputType == 'file') {
11010             input.style = 'overflow:hidden'; // why not in CSS?
11011         }
11012         
11013         if(this.capture.length){
11014             input.capture = this.capture;
11015         }
11016         
11017         if(this.accept.length){
11018             input.accept = this.accept + "/*";
11019         }
11020         
11021         if(this.align){
11022             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11023         }
11024         
11025         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11026             input.maxLength = this.maxLength;
11027         }
11028         
11029         if (this.disabled) {
11030             input.disabled=true;
11031         }
11032         
11033         if (this.readOnly) {
11034             input.readonly=true;
11035         }
11036         
11037         if (this.name) {
11038             input.name = this.name;
11039         }
11040         
11041         if (this.size) {
11042             input.cls += ' input-' + this.size;
11043         }
11044         
11045         var settings=this;
11046         ['xs','sm','md','lg'].map(function(size){
11047             if (settings[size]) {
11048                 cfg.cls += ' col-' + size + '-' + settings[size];
11049             }
11050         });
11051         
11052         var inputblock = input;
11053         
11054         var feedback = {
11055             tag: 'span',
11056             cls: 'glyphicon form-control-feedback'
11057         };
11058             
11059         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11060             
11061             inputblock = {
11062                 cls : 'has-feedback',
11063                 cn :  [
11064                     input,
11065                     feedback
11066                 ] 
11067             };  
11068         }
11069         
11070         if (this.before || this.after) {
11071             
11072             inputblock = {
11073                 cls : 'input-group',
11074                 cn :  [] 
11075             };
11076             
11077             if (this.before && typeof(this.before) == 'string') {
11078                 
11079                 inputblock.cn.push({
11080                     tag :'span',
11081                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11082                     html : this.before
11083                 });
11084             }
11085             if (this.before && typeof(this.before) == 'object') {
11086                 this.before = Roo.factory(this.before);
11087                 
11088                 inputblock.cn.push({
11089                     tag :'span',
11090                     cls : 'roo-input-before input-group-prepend   input-group-' +
11091                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11092                 });
11093             }
11094             
11095             inputblock.cn.push(input);
11096             
11097             if (this.after && typeof(this.after) == 'string') {
11098                 inputblock.cn.push({
11099                     tag :'span',
11100                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11101                     html : this.after
11102                 });
11103             }
11104             if (this.after && typeof(this.after) == 'object') {
11105                 this.after = Roo.factory(this.after);
11106                 
11107                 inputblock.cn.push({
11108                     tag :'span',
11109                     cls : 'roo-input-after input-group-append  input-group-' +
11110                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11111                 });
11112             }
11113             
11114             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11115                 inputblock.cls += ' has-feedback';
11116                 inputblock.cn.push(feedback);
11117             }
11118         };
11119         var indicator = {
11120             tag : 'i',
11121             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11122             tooltip : 'This field is required'
11123         };
11124         if (this.allowBlank ) {
11125             indicator.style = this.allowBlank ? ' display:none' : '';
11126         }
11127         if (align ==='left' && this.fieldLabel.length) {
11128             
11129             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11130             
11131             cfg.cn = [
11132                 indicator,
11133                 {
11134                     tag: 'label',
11135                     'for' :  id,
11136                     cls : 'control-label col-form-label',
11137                     html : this.fieldLabel
11138
11139                 },
11140                 {
11141                     cls : "", 
11142                     cn: [
11143                         inputblock
11144                     ]
11145                 }
11146             ];
11147             
11148             var labelCfg = cfg.cn[1];
11149             var contentCfg = cfg.cn[2];
11150             
11151             if(this.indicatorpos == 'right'){
11152                 cfg.cn = [
11153                     {
11154                         tag: 'label',
11155                         'for' :  id,
11156                         cls : 'control-label col-form-label',
11157                         cn : [
11158                             {
11159                                 tag : 'span',
11160                                 html : this.fieldLabel
11161                             },
11162                             indicator
11163                         ]
11164                     },
11165                     {
11166                         cls : "",
11167                         cn: [
11168                             inputblock
11169                         ]
11170                     }
11171
11172                 ];
11173                 
11174                 labelCfg = cfg.cn[0];
11175                 contentCfg = cfg.cn[1];
11176             
11177             }
11178             
11179             if(this.labelWidth > 12){
11180                 labelCfg.style = "width: " + this.labelWidth + 'px';
11181             }
11182             
11183             if(this.labelWidth < 13 && this.labelmd == 0){
11184                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11185             }
11186             
11187             if(this.labellg > 0){
11188                 labelCfg.cls += ' col-lg-' + this.labellg;
11189                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11190             }
11191             
11192             if(this.labelmd > 0){
11193                 labelCfg.cls += ' col-md-' + this.labelmd;
11194                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11195             }
11196             
11197             if(this.labelsm > 0){
11198                 labelCfg.cls += ' col-sm-' + this.labelsm;
11199                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11200             }
11201             
11202             if(this.labelxs > 0){
11203                 labelCfg.cls += ' col-xs-' + this.labelxs;
11204                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11205             }
11206             
11207             
11208         } else if ( this.fieldLabel.length) {
11209                 
11210             
11211             
11212             cfg.cn = [
11213                 {
11214                     tag : 'i',
11215                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11216                     tooltip : 'This field is required',
11217                     style : this.allowBlank ? ' display:none' : '' 
11218                 },
11219                 {
11220                     tag: 'label',
11221                    //cls : 'input-group-addon',
11222                     html : this.fieldLabel
11223
11224                 },
11225
11226                inputblock
11227
11228            ];
11229            
11230            if(this.indicatorpos == 'right'){
11231        
11232                 cfg.cn = [
11233                     {
11234                         tag: 'label',
11235                        //cls : 'input-group-addon',
11236                         html : this.fieldLabel
11237
11238                     },
11239                     {
11240                         tag : 'i',
11241                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11242                         tooltip : 'This field is required',
11243                         style : this.allowBlank ? ' display:none' : '' 
11244                     },
11245
11246                    inputblock
11247
11248                ];
11249
11250             }
11251
11252         } else {
11253             
11254             cfg.cn = [
11255
11256                     inputblock
11257
11258             ];
11259                 
11260                 
11261         };
11262         
11263         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11264            cfg.cls += ' navbar-form';
11265         }
11266         
11267         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11268             // on BS4 we do this only if not form 
11269             cfg.cls += ' navbar-form';
11270             cfg.tag = 'li';
11271         }
11272         
11273         return cfg;
11274         
11275     },
11276     /**
11277      * return the real input element.
11278      */
11279     inputEl: function ()
11280     {
11281         return this.el.select('input.form-control',true).first();
11282     },
11283     
11284     tooltipEl : function()
11285     {
11286         return this.inputEl();
11287     },
11288     
11289     indicatorEl : function()
11290     {
11291         if (Roo.bootstrap.version == 4) {
11292             return false; // not enabled in v4 yet.
11293         }
11294         
11295         var indicator = this.el.select('i.roo-required-indicator',true).first();
11296         
11297         if(!indicator){
11298             return false;
11299         }
11300         
11301         return indicator;
11302         
11303     },
11304     
11305     setDisabled : function(v)
11306     {
11307         var i  = this.inputEl().dom;
11308         if (!v) {
11309             i.removeAttribute('disabled');
11310             return;
11311             
11312         }
11313         i.setAttribute('disabled','true');
11314     },
11315     initEvents : function()
11316     {
11317           
11318         this.inputEl().on("keydown" , this.fireKey,  this);
11319         this.inputEl().on("focus", this.onFocus,  this);
11320         this.inputEl().on("blur", this.onBlur,  this);
11321         
11322         this.inputEl().relayEvent('keyup', this);
11323         this.inputEl().relayEvent('paste', this);
11324         
11325         this.indicator = this.indicatorEl();
11326         
11327         if(this.indicator){
11328             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11329         }
11330  
11331         // reference to original value for reset
11332         this.originalValue = this.getValue();
11333         //Roo.form.TextField.superclass.initEvents.call(this);
11334         if(this.validationEvent == 'keyup'){
11335             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11336             this.inputEl().on('keyup', this.filterValidation, this);
11337         }
11338         else if(this.validationEvent !== false){
11339             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11340         }
11341         
11342         if(this.selectOnFocus){
11343             this.on("focus", this.preFocus, this);
11344             
11345         }
11346         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11347             this.inputEl().on("keypress", this.filterKeys, this);
11348         } else {
11349             this.inputEl().relayEvent('keypress', this);
11350         }
11351        /* if(this.grow){
11352             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11353             this.el.on("click", this.autoSize,  this);
11354         }
11355         */
11356         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11357             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11358         }
11359         
11360         if (typeof(this.before) == 'object') {
11361             this.before.render(this.el.select('.roo-input-before',true).first());
11362         }
11363         if (typeof(this.after) == 'object') {
11364             this.after.render(this.el.select('.roo-input-after',true).first());
11365         }
11366         
11367         this.inputEl().on('change', this.onChange, this);
11368         
11369     },
11370     filterValidation : function(e){
11371         if(!e.isNavKeyPress()){
11372             this.validationTask.delay(this.validationDelay);
11373         }
11374     },
11375      /**
11376      * Validates the field value
11377      * @return {Boolean} True if the value is valid, else false
11378      */
11379     validate : function(){
11380         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11381         if(this.disabled || this.validateValue(this.getRawValue())){
11382             this.markValid();
11383             return true;
11384         }
11385         
11386         this.markInvalid();
11387         return false;
11388     },
11389     
11390     
11391     /**
11392      * Validates a value according to the field's validation rules and marks the field as invalid
11393      * if the validation fails
11394      * @param {Mixed} value The value to validate
11395      * @return {Boolean} True if the value is valid, else false
11396      */
11397     validateValue : function(value)
11398     {
11399         if(this.getVisibilityEl().hasClass('hidden')){
11400             return true;
11401         }
11402         
11403         if(value.length < 1)  { // if it's blank
11404             if(this.allowBlank){
11405                 return true;
11406             }
11407             return false;
11408         }
11409         
11410         if(value.length < this.minLength){
11411             return false;
11412         }
11413         if(value.length > this.maxLength){
11414             return false;
11415         }
11416         if(this.vtype){
11417             var vt = Roo.form.VTypes;
11418             if(!vt[this.vtype](value, this)){
11419                 return false;
11420             }
11421         }
11422         if(typeof this.validator == "function"){
11423             var msg = this.validator(value);
11424             if(msg !== true){
11425                 return false;
11426             }
11427             if (typeof(msg) == 'string') {
11428                 this.invalidText = msg;
11429             }
11430         }
11431         
11432         if(this.regex && !this.regex.test(value)){
11433             return false;
11434         }
11435         
11436         return true;
11437     },
11438     
11439      // private
11440     fireKey : function(e){
11441         //Roo.log('field ' + e.getKey());
11442         if(e.isNavKeyPress()){
11443             this.fireEvent("specialkey", this, e);
11444         }
11445     },
11446     focus : function (selectText){
11447         if(this.rendered){
11448             this.inputEl().focus();
11449             if(selectText === true){
11450                 this.inputEl().dom.select();
11451             }
11452         }
11453         return this;
11454     } ,
11455     
11456     onFocus : function(){
11457         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11458            // this.el.addClass(this.focusClass);
11459         }
11460         if(!this.hasFocus){
11461             this.hasFocus = true;
11462             this.startValue = this.getValue();
11463             this.fireEvent("focus", this);
11464         }
11465     },
11466     
11467     beforeBlur : Roo.emptyFn,
11468
11469     
11470     // private
11471     onBlur : function(){
11472         this.beforeBlur();
11473         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11474             //this.el.removeClass(this.focusClass);
11475         }
11476         this.hasFocus = false;
11477         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11478             this.validate();
11479         }
11480         var v = this.getValue();
11481         if(String(v) !== String(this.startValue)){
11482             this.fireEvent('change', this, v, this.startValue);
11483         }
11484         this.fireEvent("blur", this);
11485     },
11486     
11487     onChange : function(e)
11488     {
11489         var v = this.getValue();
11490         if(String(v) !== String(this.startValue)){
11491             this.fireEvent('change', this, v, this.startValue);
11492         }
11493         
11494     },
11495     
11496     /**
11497      * Resets the current field value to the originally loaded value and clears any validation messages
11498      */
11499     reset : function(){
11500         this.setValue(this.originalValue);
11501         this.validate();
11502     },
11503      /**
11504      * Returns the name of the field
11505      * @return {Mixed} name The name field
11506      */
11507     getName: function(){
11508         return this.name;
11509     },
11510      /**
11511      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11512      * @return {Mixed} value The field value
11513      */
11514     getValue : function(){
11515         
11516         var v = this.inputEl().getValue();
11517         
11518         return v;
11519     },
11520     /**
11521      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11522      * @return {Mixed} value The field value
11523      */
11524     getRawValue : function(){
11525         var v = this.inputEl().getValue();
11526         
11527         return v;
11528     },
11529     
11530     /**
11531      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11532      * @param {Mixed} value The value to set
11533      */
11534     setRawValue : function(v){
11535         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11536     },
11537     
11538     selectText : function(start, end){
11539         var v = this.getRawValue();
11540         if(v.length > 0){
11541             start = start === undefined ? 0 : start;
11542             end = end === undefined ? v.length : end;
11543             var d = this.inputEl().dom;
11544             if(d.setSelectionRange){
11545                 d.setSelectionRange(start, end);
11546             }else if(d.createTextRange){
11547                 var range = d.createTextRange();
11548                 range.moveStart("character", start);
11549                 range.moveEnd("character", v.length-end);
11550                 range.select();
11551             }
11552         }
11553     },
11554     
11555     /**
11556      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11557      * @param {Mixed} value The value to set
11558      */
11559     setValue : function(v){
11560         this.value = v;
11561         if(this.rendered){
11562             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11563             this.validate();
11564         }
11565     },
11566     
11567     /*
11568     processValue : function(value){
11569         if(this.stripCharsRe){
11570             var newValue = value.replace(this.stripCharsRe, '');
11571             if(newValue !== value){
11572                 this.setRawValue(newValue);
11573                 return newValue;
11574             }
11575         }
11576         return value;
11577     },
11578   */
11579     preFocus : function(){
11580         
11581         if(this.selectOnFocus){
11582             this.inputEl().dom.select();
11583         }
11584     },
11585     filterKeys : function(e){
11586         var k = e.getKey();
11587         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11588             return;
11589         }
11590         var c = e.getCharCode(), cc = String.fromCharCode(c);
11591         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11592             return;
11593         }
11594         if(!this.maskRe.test(cc)){
11595             e.stopEvent();
11596         }
11597     },
11598      /**
11599      * Clear any invalid styles/messages for this field
11600      */
11601     clearInvalid : function(){
11602         
11603         if(!this.el || this.preventMark){ // not rendered
11604             return;
11605         }
11606         
11607         
11608         this.el.removeClass([this.invalidClass, 'is-invalid']);
11609         
11610         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11611             
11612             var feedback = this.el.select('.form-control-feedback', true).first();
11613             
11614             if(feedback){
11615                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11616             }
11617             
11618         }
11619         
11620         if(this.indicator){
11621             this.indicator.removeClass('visible');
11622             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11623         }
11624         
11625         this.fireEvent('valid', this);
11626     },
11627     
11628      /**
11629      * Mark this field as valid
11630      */
11631     markValid : function()
11632     {
11633         if(!this.el  || this.preventMark){ // not rendered...
11634             return;
11635         }
11636         
11637         this.el.removeClass([this.invalidClass, this.validClass]);
11638         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11639
11640         var feedback = this.el.select('.form-control-feedback', true).first();
11641             
11642         if(feedback){
11643             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11644         }
11645         
11646         if(this.indicator){
11647             this.indicator.removeClass('visible');
11648             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11649         }
11650         
11651         if(this.disabled){
11652             return;
11653         }
11654         
11655            
11656         if(this.allowBlank && !this.getRawValue().length){
11657             return;
11658         }
11659         if (Roo.bootstrap.version == 3) {
11660             this.el.addClass(this.validClass);
11661         } else {
11662             this.inputEl().addClass('is-valid');
11663         }
11664
11665         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11666             
11667             var feedback = this.el.select('.form-control-feedback', true).first();
11668             
11669             if(feedback){
11670                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11671                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11672             }
11673             
11674         }
11675         
11676         this.fireEvent('valid', this);
11677     },
11678     
11679      /**
11680      * Mark this field as invalid
11681      * @param {String} msg The validation message
11682      */
11683     markInvalid : function(msg)
11684     {
11685         if(!this.el  || this.preventMark){ // not rendered
11686             return;
11687         }
11688         
11689         this.el.removeClass([this.invalidClass, this.validClass]);
11690         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11691         
11692         var feedback = this.el.select('.form-control-feedback', true).first();
11693             
11694         if(feedback){
11695             this.el.select('.form-control-feedback', true).first().removeClass(
11696                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11697         }
11698
11699         if(this.disabled){
11700             return;
11701         }
11702         
11703         if(this.allowBlank && !this.getRawValue().length){
11704             return;
11705         }
11706         
11707         if(this.indicator){
11708             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11709             this.indicator.addClass('visible');
11710         }
11711         if (Roo.bootstrap.version == 3) {
11712             this.el.addClass(this.invalidClass);
11713         } else {
11714             this.inputEl().addClass('is-invalid');
11715         }
11716         
11717         
11718         
11719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11720             
11721             var feedback = this.el.select('.form-control-feedback', true).first();
11722             
11723             if(feedback){
11724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11725                 
11726                 if(this.getValue().length || this.forceFeedback){
11727                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11728                 }
11729                 
11730             }
11731             
11732         }
11733         
11734         this.fireEvent('invalid', this, msg);
11735     },
11736     // private
11737     SafariOnKeyDown : function(event)
11738     {
11739         // this is a workaround for a password hang bug on chrome/ webkit.
11740         if (this.inputEl().dom.type != 'password') {
11741             return;
11742         }
11743         
11744         var isSelectAll = false;
11745         
11746         if(this.inputEl().dom.selectionEnd > 0){
11747             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11748         }
11749         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11750             event.preventDefault();
11751             this.setValue('');
11752             return;
11753         }
11754         
11755         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11756             
11757             event.preventDefault();
11758             // this is very hacky as keydown always get's upper case.
11759             //
11760             var cc = String.fromCharCode(event.getCharCode());
11761             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11762             
11763         }
11764     },
11765     adjustWidth : function(tag, w){
11766         tag = tag.toLowerCase();
11767         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11768             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11769                 if(tag == 'input'){
11770                     return w + 2;
11771                 }
11772                 if(tag == 'textarea'){
11773                     return w-2;
11774                 }
11775             }else if(Roo.isOpera){
11776                 if(tag == 'input'){
11777                     return w + 2;
11778                 }
11779                 if(tag == 'textarea'){
11780                     return w-2;
11781                 }
11782             }
11783         }
11784         return w;
11785     },
11786     
11787     setFieldLabel : function(v)
11788     {
11789         if(!this.rendered){
11790             return;
11791         }
11792         
11793         if(this.indicatorEl()){
11794             var ar = this.el.select('label > span',true);
11795             
11796             if (ar.elements.length) {
11797                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11798                 this.fieldLabel = v;
11799                 return;
11800             }
11801             
11802             var br = this.el.select('label',true);
11803             
11804             if(br.elements.length) {
11805                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11806                 this.fieldLabel = v;
11807                 return;
11808             }
11809             
11810             Roo.log('Cannot Found any of label > span || label in input');
11811             return;
11812         }
11813         
11814         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11815         this.fieldLabel = v;
11816         
11817         
11818     }
11819 });
11820
11821  
11822 /*
11823  * - LGPL
11824  *
11825  * Input
11826  * 
11827  */
11828
11829 /**
11830  * @class Roo.bootstrap.TextArea
11831  * @extends Roo.bootstrap.Input
11832  * Bootstrap TextArea class
11833  * @cfg {Number} cols Specifies the visible width of a text area
11834  * @cfg {Number} rows Specifies the visible number of lines in a text area
11835  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11836  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11837  * @cfg {string} html text
11838  * 
11839  * @constructor
11840  * Create a new TextArea
11841  * @param {Object} config The config object
11842  */
11843
11844 Roo.bootstrap.TextArea = function(config){
11845     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11846    
11847 };
11848
11849 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11850      
11851     cols : false,
11852     rows : 5,
11853     readOnly : false,
11854     warp : 'soft',
11855     resize : false,
11856     value: false,
11857     html: false,
11858     
11859     getAutoCreate : function(){
11860         
11861         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11862         
11863         var id = Roo.id();
11864         
11865         var cfg = {};
11866         
11867         if(this.inputType != 'hidden'){
11868             cfg.cls = 'form-group' //input-group
11869         }
11870         
11871         var input =  {
11872             tag: 'textarea',
11873             id : id,
11874             warp : this.warp,
11875             rows : this.rows,
11876             value : this.value || '',
11877             html: this.html || '',
11878             cls : 'form-control',
11879             placeholder : this.placeholder || '' 
11880             
11881         };
11882         
11883         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11884             input.maxLength = this.maxLength;
11885         }
11886         
11887         if(this.resize){
11888             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11889         }
11890         
11891         if(this.cols){
11892             input.cols = this.cols;
11893         }
11894         
11895         if (this.readOnly) {
11896             input.readonly = true;
11897         }
11898         
11899         if (this.name) {
11900             input.name = this.name;
11901         }
11902         
11903         if (this.size) {
11904             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11905         }
11906         
11907         var settings=this;
11908         ['xs','sm','md','lg'].map(function(size){
11909             if (settings[size]) {
11910                 cfg.cls += ' col-' + size + '-' + settings[size];
11911             }
11912         });
11913         
11914         var inputblock = input;
11915         
11916         if(this.hasFeedback && !this.allowBlank){
11917             
11918             var feedback = {
11919                 tag: 'span',
11920                 cls: 'glyphicon form-control-feedback'
11921             };
11922
11923             inputblock = {
11924                 cls : 'has-feedback',
11925                 cn :  [
11926                     input,
11927                     feedback
11928                 ] 
11929             };  
11930         }
11931         
11932         
11933         if (this.before || this.after) {
11934             
11935             inputblock = {
11936                 cls : 'input-group',
11937                 cn :  [] 
11938             };
11939             if (this.before) {
11940                 inputblock.cn.push({
11941                     tag :'span',
11942                     cls : 'input-group-addon',
11943                     html : this.before
11944                 });
11945             }
11946             
11947             inputblock.cn.push(input);
11948             
11949             if(this.hasFeedback && !this.allowBlank){
11950                 inputblock.cls += ' has-feedback';
11951                 inputblock.cn.push(feedback);
11952             }
11953             
11954             if (this.after) {
11955                 inputblock.cn.push({
11956                     tag :'span',
11957                     cls : 'input-group-addon',
11958                     html : this.after
11959                 });
11960             }
11961             
11962         }
11963         
11964         if (align ==='left' && this.fieldLabel.length) {
11965             cfg.cn = [
11966                 {
11967                     tag: 'label',
11968                     'for' :  id,
11969                     cls : 'control-label',
11970                     html : this.fieldLabel
11971                 },
11972                 {
11973                     cls : "",
11974                     cn: [
11975                         inputblock
11976                     ]
11977                 }
11978
11979             ];
11980             
11981             if(this.labelWidth > 12){
11982                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11983             }
11984
11985             if(this.labelWidth < 13 && this.labelmd == 0){
11986                 this.labelmd = this.labelWidth;
11987             }
11988
11989             if(this.labellg > 0){
11990                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11991                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11992             }
11993
11994             if(this.labelmd > 0){
11995                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11996                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11997             }
11998
11999             if(this.labelsm > 0){
12000                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12001                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12002             }
12003
12004             if(this.labelxs > 0){
12005                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12006                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12007             }
12008             
12009         } else if ( this.fieldLabel.length) {
12010             cfg.cn = [
12011
12012                {
12013                    tag: 'label',
12014                    //cls : 'input-group-addon',
12015                    html : this.fieldLabel
12016
12017                },
12018
12019                inputblock
12020
12021            ];
12022
12023         } else {
12024
12025             cfg.cn = [
12026
12027                 inputblock
12028
12029             ];
12030                 
12031         }
12032         
12033         if (this.disabled) {
12034             input.disabled=true;
12035         }
12036         
12037         return cfg;
12038         
12039     },
12040     /**
12041      * return the real textarea element.
12042      */
12043     inputEl: function ()
12044     {
12045         return this.el.select('textarea.form-control',true).first();
12046     },
12047     
12048     /**
12049      * Clear any invalid styles/messages for this field
12050      */
12051     clearInvalid : function()
12052     {
12053         
12054         if(!this.el || this.preventMark){ // not rendered
12055             return;
12056         }
12057         
12058         var label = this.el.select('label', true).first();
12059         var icon = this.el.select('i.fa-star', true).first();
12060         
12061         if(label && icon){
12062             icon.remove();
12063         }
12064         this.el.removeClass( this.validClass);
12065         this.inputEl().removeClass('is-invalid');
12066          
12067         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12068             
12069             var feedback = this.el.select('.form-control-feedback', true).first();
12070             
12071             if(feedback){
12072                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12073             }
12074             
12075         }
12076         
12077         this.fireEvent('valid', this);
12078     },
12079     
12080      /**
12081      * Mark this field as valid
12082      */
12083     markValid : function()
12084     {
12085         if(!this.el  || this.preventMark){ // not rendered
12086             return;
12087         }
12088         
12089         this.el.removeClass([this.invalidClass, this.validClass]);
12090         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12091         
12092         var feedback = this.el.select('.form-control-feedback', true).first();
12093             
12094         if(feedback){
12095             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12096         }
12097
12098         if(this.disabled || this.allowBlank){
12099             return;
12100         }
12101         
12102         var label = this.el.select('label', true).first();
12103         var icon = this.el.select('i.fa-star', true).first();
12104         
12105         if(label && icon){
12106             icon.remove();
12107         }
12108         if (Roo.bootstrap.version == 3) {
12109             this.el.addClass(this.validClass);
12110         } else {
12111             this.inputEl().addClass('is-valid');
12112         }
12113         
12114         
12115         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12116             
12117             var feedback = this.el.select('.form-control-feedback', true).first();
12118             
12119             if(feedback){
12120                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12121                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12122             }
12123             
12124         }
12125         
12126         this.fireEvent('valid', this);
12127     },
12128     
12129      /**
12130      * Mark this field as invalid
12131      * @param {String} msg The validation message
12132      */
12133     markInvalid : function(msg)
12134     {
12135         if(!this.el  || this.preventMark){ // not rendered
12136             return;
12137         }
12138         
12139         this.el.removeClass([this.invalidClass, this.validClass]);
12140         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12141         
12142         var feedback = this.el.select('.form-control-feedback', true).first();
12143             
12144         if(feedback){
12145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12146         }
12147
12148         if(this.disabled || this.allowBlank){
12149             return;
12150         }
12151         
12152         var label = this.el.select('label', true).first();
12153         var icon = this.el.select('i.fa-star', true).first();
12154         
12155         if(!this.getValue().length && label && !icon){
12156             this.el.createChild({
12157                 tag : 'i',
12158                 cls : 'text-danger fa fa-lg fa-star',
12159                 tooltip : 'This field is required',
12160                 style : 'margin-right:5px;'
12161             }, label, true);
12162         }
12163         
12164         if (Roo.bootstrap.version == 3) {
12165             this.el.addClass(this.invalidClass);
12166         } else {
12167             this.inputEl().addClass('is-invalid');
12168         }
12169         
12170         // fixme ... this may be depricated need to test..
12171         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12172             
12173             var feedback = this.el.select('.form-control-feedback', true).first();
12174             
12175             if(feedback){
12176                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12177                 
12178                 if(this.getValue().length || this.forceFeedback){
12179                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12180                 }
12181                 
12182             }
12183             
12184         }
12185         
12186         this.fireEvent('invalid', this, msg);
12187     }
12188 });
12189
12190  
12191 /*
12192  * - LGPL
12193  *
12194  * trigger field - base class for combo..
12195  * 
12196  */
12197  
12198 /**
12199  * @class Roo.bootstrap.TriggerField
12200  * @extends Roo.bootstrap.Input
12201  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12202  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12203  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12204  * for which you can provide a custom implementation.  For example:
12205  * <pre><code>
12206 var trigger = new Roo.bootstrap.TriggerField();
12207 trigger.onTriggerClick = myTriggerFn;
12208 trigger.applyTo('my-field');
12209 </code></pre>
12210  *
12211  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12212  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12213  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12214  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12215  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12216
12217  * @constructor
12218  * Create a new TriggerField.
12219  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12220  * to the base TextField)
12221  */
12222 Roo.bootstrap.TriggerField = function(config){
12223     this.mimicing = false;
12224     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12225 };
12226
12227 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12228     /**
12229      * @cfg {String} triggerClass A CSS class to apply to the trigger
12230      */
12231      /**
12232      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12233      */
12234     hideTrigger:false,
12235
12236     /**
12237      * @cfg {Boolean} removable (true|false) special filter default false
12238      */
12239     removable : false,
12240     
12241     /** @cfg {Boolean} grow @hide */
12242     /** @cfg {Number} growMin @hide */
12243     /** @cfg {Number} growMax @hide */
12244
12245     /**
12246      * @hide 
12247      * @method
12248      */
12249     autoSize: Roo.emptyFn,
12250     // private
12251     monitorTab : true,
12252     // private
12253     deferHeight : true,
12254
12255     
12256     actionMode : 'wrap',
12257     
12258     caret : false,
12259     
12260     
12261     getAutoCreate : function(){
12262        
12263         var align = this.labelAlign || this.parentLabelAlign();
12264         
12265         var id = Roo.id();
12266         
12267         var cfg = {
12268             cls: 'form-group' //input-group
12269         };
12270         
12271         
12272         var input =  {
12273             tag: 'input',
12274             id : id,
12275             type : this.inputType,
12276             cls : 'form-control',
12277             autocomplete: 'new-password',
12278             placeholder : this.placeholder || '' 
12279             
12280         };
12281         if (this.name) {
12282             input.name = this.name;
12283         }
12284         if (this.size) {
12285             input.cls += ' input-' + this.size;
12286         }
12287         
12288         if (this.disabled) {
12289             input.disabled=true;
12290         }
12291         
12292         var inputblock = input;
12293         
12294         if(this.hasFeedback && !this.allowBlank){
12295             
12296             var feedback = {
12297                 tag: 'span',
12298                 cls: 'glyphicon form-control-feedback'
12299             };
12300             
12301             if(this.removable && !this.editable  ){
12302                 inputblock = {
12303                     cls : 'has-feedback',
12304                     cn :  [
12305                         inputblock,
12306                         {
12307                             tag: 'button',
12308                             html : 'x',
12309                             cls : 'roo-combo-removable-btn close'
12310                         },
12311                         feedback
12312                     ] 
12313                 };
12314             } else {
12315                 inputblock = {
12316                     cls : 'has-feedback',
12317                     cn :  [
12318                         inputblock,
12319                         feedback
12320                     ] 
12321                 };
12322             }
12323
12324         } else {
12325             if(this.removable && !this.editable ){
12326                 inputblock = {
12327                     cls : 'roo-removable',
12328                     cn :  [
12329                         inputblock,
12330                         {
12331                             tag: 'button',
12332                             html : 'x',
12333                             cls : 'roo-combo-removable-btn close'
12334                         }
12335                     ] 
12336                 };
12337             }
12338         }
12339         
12340         if (this.before || this.after) {
12341             
12342             inputblock = {
12343                 cls : 'input-group',
12344                 cn :  [] 
12345             };
12346             if (this.before) {
12347                 inputblock.cn.push({
12348                     tag :'span',
12349                     cls : 'input-group-addon input-group-prepend input-group-text',
12350                     html : this.before
12351                 });
12352             }
12353             
12354             inputblock.cn.push(input);
12355             
12356             if(this.hasFeedback && !this.allowBlank){
12357                 inputblock.cls += ' has-feedback';
12358                 inputblock.cn.push(feedback);
12359             }
12360             
12361             if (this.after) {
12362                 inputblock.cn.push({
12363                     tag :'span',
12364                     cls : 'input-group-addon input-group-append input-group-text',
12365                     html : this.after
12366                 });
12367             }
12368             
12369         };
12370         
12371       
12372         
12373         var ibwrap = inputblock;
12374         
12375         if(this.multiple){
12376             ibwrap = {
12377                 tag: 'ul',
12378                 cls: 'roo-select2-choices',
12379                 cn:[
12380                     {
12381                         tag: 'li',
12382                         cls: 'roo-select2-search-field',
12383                         cn: [
12384
12385                             inputblock
12386                         ]
12387                     }
12388                 ]
12389             };
12390                 
12391         }
12392         
12393         var combobox = {
12394             cls: 'roo-select2-container input-group',
12395             cn: [
12396                  {
12397                     tag: 'input',
12398                     type : 'hidden',
12399                     cls: 'form-hidden-field'
12400                 },
12401                 ibwrap
12402             ]
12403         };
12404         
12405         if(!this.multiple && this.showToggleBtn){
12406             
12407             var caret = {
12408                         tag: 'span',
12409                         cls: 'caret'
12410              };
12411             if (this.caret != false) {
12412                 caret = {
12413                      tag: 'i',
12414                      cls: 'fa fa-' + this.caret
12415                 };
12416                 
12417             }
12418             
12419             combobox.cn.push({
12420                 tag :'span',
12421                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12422                 cn : [
12423                     Roo.bootstrap.version == 3 ? caret : '',
12424                     {
12425                         tag: 'span',
12426                         cls: 'combobox-clear',
12427                         cn  : [
12428                             {
12429                                 tag : 'i',
12430                                 cls: 'icon-remove'
12431                             }
12432                         ]
12433                     }
12434                 ]
12435
12436             })
12437         }
12438         
12439         if(this.multiple){
12440             combobox.cls += ' roo-select2-container-multi';
12441         }
12442          var indicator = {
12443             tag : 'i',
12444             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12445             tooltip : 'This field is required'
12446         };
12447         if (Roo.bootstrap.version == 4) {
12448             indicator = {
12449                 tag : 'i',
12450                 style : 'display:none'
12451             };
12452         }
12453         
12454         
12455         if (align ==='left' && this.fieldLabel.length) {
12456             
12457             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12458
12459             cfg.cn = [
12460                 indicator,
12461                 {
12462                     tag: 'label',
12463                     'for' :  id,
12464                     cls : 'control-label',
12465                     html : this.fieldLabel
12466
12467                 },
12468                 {
12469                     cls : "", 
12470                     cn: [
12471                         combobox
12472                     ]
12473                 }
12474
12475             ];
12476             
12477             var labelCfg = cfg.cn[1];
12478             var contentCfg = cfg.cn[2];
12479             
12480             if(this.indicatorpos == 'right'){
12481                 cfg.cn = [
12482                     {
12483                         tag: 'label',
12484                         'for' :  id,
12485                         cls : 'control-label',
12486                         cn : [
12487                             {
12488                                 tag : 'span',
12489                                 html : this.fieldLabel
12490                             },
12491                             indicator
12492                         ]
12493                     },
12494                     {
12495                         cls : "", 
12496                         cn: [
12497                             combobox
12498                         ]
12499                     }
12500
12501                 ];
12502                 
12503                 labelCfg = cfg.cn[0];
12504                 contentCfg = cfg.cn[1];
12505             }
12506             
12507             if(this.labelWidth > 12){
12508                 labelCfg.style = "width: " + this.labelWidth + 'px';
12509             }
12510             
12511             if(this.labelWidth < 13 && this.labelmd == 0){
12512                 this.labelmd = this.labelWidth;
12513             }
12514             
12515             if(this.labellg > 0){
12516                 labelCfg.cls += ' col-lg-' + this.labellg;
12517                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12518             }
12519             
12520             if(this.labelmd > 0){
12521                 labelCfg.cls += ' col-md-' + this.labelmd;
12522                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12523             }
12524             
12525             if(this.labelsm > 0){
12526                 labelCfg.cls += ' col-sm-' + this.labelsm;
12527                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12528             }
12529             
12530             if(this.labelxs > 0){
12531                 labelCfg.cls += ' col-xs-' + this.labelxs;
12532                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12533             }
12534             
12535         } else if ( this.fieldLabel.length) {
12536 //                Roo.log(" label");
12537             cfg.cn = [
12538                 indicator,
12539                {
12540                    tag: 'label',
12541                    //cls : 'input-group-addon',
12542                    html : this.fieldLabel
12543
12544                },
12545
12546                combobox
12547
12548             ];
12549             
12550             if(this.indicatorpos == 'right'){
12551                 
12552                 cfg.cn = [
12553                     {
12554                        tag: 'label',
12555                        cn : [
12556                            {
12557                                tag : 'span',
12558                                html : this.fieldLabel
12559                            },
12560                            indicator
12561                        ]
12562
12563                     },
12564                     combobox
12565
12566                 ];
12567
12568             }
12569
12570         } else {
12571             
12572 //                Roo.log(" no label && no align");
12573                 cfg = combobox
12574                      
12575                 
12576         }
12577         
12578         var settings=this;
12579         ['xs','sm','md','lg'].map(function(size){
12580             if (settings[size]) {
12581                 cfg.cls += ' col-' + size + '-' + settings[size];
12582             }
12583         });
12584         
12585         return cfg;
12586         
12587     },
12588     
12589     
12590     
12591     // private
12592     onResize : function(w, h){
12593 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12594 //        if(typeof w == 'number'){
12595 //            var x = w - this.trigger.getWidth();
12596 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12597 //            this.trigger.setStyle('left', x+'px');
12598 //        }
12599     },
12600
12601     // private
12602     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12603
12604     // private
12605     getResizeEl : function(){
12606         return this.inputEl();
12607     },
12608
12609     // private
12610     getPositionEl : function(){
12611         return this.inputEl();
12612     },
12613
12614     // private
12615     alignErrorIcon : function(){
12616         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12617     },
12618
12619     // private
12620     initEvents : function(){
12621         
12622         this.createList();
12623         
12624         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12625         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12626         if(!this.multiple && this.showToggleBtn){
12627             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12628             if(this.hideTrigger){
12629                 this.trigger.setDisplayed(false);
12630             }
12631             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12632         }
12633         
12634         if(this.multiple){
12635             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12636         }
12637         
12638         if(this.removable && !this.editable && !this.tickable){
12639             var close = this.closeTriggerEl();
12640             
12641             if(close){
12642                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12643                 close.on('click', this.removeBtnClick, this, close);
12644             }
12645         }
12646         
12647         //this.trigger.addClassOnOver('x-form-trigger-over');
12648         //this.trigger.addClassOnClick('x-form-trigger-click');
12649         
12650         //if(!this.width){
12651         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12652         //}
12653     },
12654     
12655     closeTriggerEl : function()
12656     {
12657         var close = this.el.select('.roo-combo-removable-btn', true).first();
12658         return close ? close : false;
12659     },
12660     
12661     removeBtnClick : function(e, h, el)
12662     {
12663         e.preventDefault();
12664         
12665         if(this.fireEvent("remove", this) !== false){
12666             this.reset();
12667             this.fireEvent("afterremove", this)
12668         }
12669     },
12670     
12671     createList : function()
12672     {
12673         this.list = Roo.get(document.body).createChild({
12674             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12675             cls: 'typeahead typeahead-long dropdown-menu shadow',
12676             style: 'display:none'
12677         });
12678         
12679         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12680         
12681     },
12682
12683     // private
12684     initTrigger : function(){
12685        
12686     },
12687
12688     // private
12689     onDestroy : function(){
12690         if(this.trigger){
12691             this.trigger.removeAllListeners();
12692           //  this.trigger.remove();
12693         }
12694         //if(this.wrap){
12695         //    this.wrap.remove();
12696         //}
12697         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12698     },
12699
12700     // private
12701     onFocus : function(){
12702         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12703         /*
12704         if(!this.mimicing){
12705             this.wrap.addClass('x-trigger-wrap-focus');
12706             this.mimicing = true;
12707             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12708             if(this.monitorTab){
12709                 this.el.on("keydown", this.checkTab, this);
12710             }
12711         }
12712         */
12713     },
12714
12715     // private
12716     checkTab : function(e){
12717         if(e.getKey() == e.TAB){
12718             this.triggerBlur();
12719         }
12720     },
12721
12722     // private
12723     onBlur : function(){
12724         // do nothing
12725     },
12726
12727     // private
12728     mimicBlur : function(e, t){
12729         /*
12730         if(!this.wrap.contains(t) && this.validateBlur()){
12731             this.triggerBlur();
12732         }
12733         */
12734     },
12735
12736     // private
12737     triggerBlur : function(){
12738         this.mimicing = false;
12739         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12740         if(this.monitorTab){
12741             this.el.un("keydown", this.checkTab, this);
12742         }
12743         //this.wrap.removeClass('x-trigger-wrap-focus');
12744         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12745     },
12746
12747     // private
12748     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12749     validateBlur : function(e, t){
12750         return true;
12751     },
12752
12753     // private
12754     onDisable : function(){
12755         this.inputEl().dom.disabled = true;
12756         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12757         //if(this.wrap){
12758         //    this.wrap.addClass('x-item-disabled');
12759         //}
12760     },
12761
12762     // private
12763     onEnable : function(){
12764         this.inputEl().dom.disabled = false;
12765         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12766         //if(this.wrap){
12767         //    this.el.removeClass('x-item-disabled');
12768         //}
12769     },
12770
12771     // private
12772     onShow : function(){
12773         var ae = this.getActionEl();
12774         
12775         if(ae){
12776             ae.dom.style.display = '';
12777             ae.dom.style.visibility = 'visible';
12778         }
12779     },
12780
12781     // private
12782     
12783     onHide : function(){
12784         var ae = this.getActionEl();
12785         ae.dom.style.display = 'none';
12786     },
12787
12788     /**
12789      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12790      * by an implementing function.
12791      * @method
12792      * @param {EventObject} e
12793      */
12794     onTriggerClick : Roo.emptyFn
12795 });
12796  
12797 /*
12798 * Licence: LGPL
12799 */
12800
12801 /**
12802  * @class Roo.bootstrap.CardUploader
12803  * @extends Roo.bootstrap.Button
12804  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12805  * @cfg {Number} errorTimeout default 3000
12806  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12807  * @cfg {Array}  html The button text.
12808
12809  *
12810  * @constructor
12811  * Create a new CardUploader
12812  * @param {Object} config The config object
12813  */
12814
12815 Roo.bootstrap.CardUploader = function(config){
12816     
12817  
12818     
12819     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12820     
12821     
12822     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12823         return r.data.id
12824      });
12825     
12826      this.addEvents({
12827          // raw events
12828         /**
12829          * @event preview
12830          * When a image is clicked on - and needs to display a slideshow or similar..
12831          * @param {Roo.bootstrap.Card} this
12832          * @param {Object} The image information data 
12833          *
12834          */
12835         'preview' : true,
12836          /**
12837          * @event download
12838          * When a the download link is clicked
12839          * @param {Roo.bootstrap.Card} this
12840          * @param {Object} The image information data  contains 
12841          */
12842         'download' : true
12843         
12844     });
12845 };
12846  
12847 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12848     
12849      
12850     errorTimeout : 3000,
12851      
12852     images : false,
12853    
12854     fileCollection : false,
12855     allowBlank : true,
12856     
12857     getAutoCreate : function()
12858     {
12859         
12860         var cfg =  {
12861             cls :'form-group' ,
12862             cn : [
12863                
12864                 {
12865                     tag: 'label',
12866                    //cls : 'input-group-addon',
12867                     html : this.fieldLabel
12868
12869                 },
12870
12871                 {
12872                     tag: 'input',
12873                     type : 'hidden',
12874                     name : this.name,
12875                     value : this.value,
12876                     cls : 'd-none  form-control'
12877                 },
12878                 
12879                 {
12880                     tag: 'input',
12881                     multiple : 'multiple',
12882                     type : 'file',
12883                     cls : 'd-none  roo-card-upload-selector'
12884                 },
12885                 
12886                 {
12887                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12888                 },
12889                 {
12890                     cls : 'card-columns roo-card-uploader-container'
12891                 }
12892
12893             ]
12894         };
12895            
12896          
12897         return cfg;
12898     },
12899     
12900     getChildContainer : function() /// what children are added to.
12901     {
12902         return this.containerEl;
12903     },
12904    
12905     getButtonContainer : function() /// what children are added to.
12906     {
12907         return this.el.select(".roo-card-uploader-button-container").first();
12908     },
12909    
12910     initEvents : function()
12911     {
12912         
12913         Roo.bootstrap.Input.prototype.initEvents.call(this);
12914         
12915         var t = this;
12916         this.addxtype({
12917             xns: Roo.bootstrap,
12918
12919             xtype : 'Button',
12920             container_method : 'getButtonContainer' ,            
12921             html :  this.html, // fix changable?
12922             cls : 'w-100 ',
12923             listeners : {
12924                 'click' : function(btn, e) {
12925                     t.onClick(e);
12926                 }
12927             }
12928         });
12929         
12930         
12931         
12932         
12933         this.urlAPI = (window.createObjectURL && window) || 
12934                                 (window.URL && URL.revokeObjectURL && URL) || 
12935                                 (window.webkitURL && webkitURL);
12936                         
12937          
12938          
12939          
12940         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12941         
12942         this.selectorEl.on('change', this.onFileSelected, this);
12943         if (this.images) {
12944             var t = this;
12945             this.images.forEach(function(img) {
12946                 t.addCard(img)
12947             });
12948             this.images = false;
12949         }
12950         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12951          
12952        
12953     },
12954     
12955    
12956     onClick : function(e)
12957     {
12958         e.preventDefault();
12959          
12960         this.selectorEl.dom.click();
12961          
12962     },
12963     
12964     onFileSelected : function(e)
12965     {
12966         e.preventDefault();
12967         
12968         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12969             return;
12970         }
12971         
12972         Roo.each(this.selectorEl.dom.files, function(file){    
12973             this.addFile(file);
12974         }, this);
12975          
12976     },
12977     
12978       
12979     
12980       
12981     
12982     addFile : function(file)
12983     {
12984            
12985         if(typeof(file) === 'string'){
12986             throw "Add file by name?"; // should not happen
12987             return;
12988         }
12989         
12990         if(!file || !this.urlAPI){
12991             return;
12992         }
12993         
12994         // file;
12995         // file.type;
12996         
12997         var _this = this;
12998         
12999         
13000         var url = _this.urlAPI.createObjectURL( file);
13001            
13002         this.addCard({
13003             id : Roo.bootstrap.CardUploader.ID--,
13004             is_uploaded : false,
13005             src : url,
13006             srcfile : file,
13007             title : file.name,
13008             mimetype : file.type,
13009             preview : false,
13010             is_deleted : 0
13011         });
13012         
13013     },
13014     
13015     /**
13016      * addCard - add an Attachment to the uploader
13017      * @param data - the data about the image to upload
13018      *
13019      * {
13020           id : 123
13021           title : "Title of file",
13022           is_uploaded : false,
13023           src : "http://.....",
13024           srcfile : { the File upload object },
13025           mimetype : file.type,
13026           preview : false,
13027           is_deleted : 0
13028           .. any other data...
13029         }
13030      *
13031      * 
13032     */
13033     
13034     addCard : function (data)
13035     {
13036         // hidden input element?
13037         // if the file is not an image...
13038         //then we need to use something other that and header_image
13039         var t = this;
13040         //   remove.....
13041         var footer = [
13042             {
13043                 xns : Roo.bootstrap,
13044                 xtype : 'CardFooter',
13045                  items: [
13046                     {
13047                         xns : Roo.bootstrap,
13048                         xtype : 'Element',
13049                         cls : 'd-flex',
13050                         items : [
13051                             
13052                             {
13053                                 xns : Roo.bootstrap,
13054                                 xtype : 'Button',
13055                                 html : String.format("<small>{0}</small>", data.title),
13056                                 cls : 'col-10 text-left',
13057                                 size: 'sm',
13058                                 weight: 'link',
13059                                 fa : 'download',
13060                                 listeners : {
13061                                     click : function() {
13062                                      
13063                                         t.fireEvent( "download", t, data );
13064                                     }
13065                                 }
13066                             },
13067                           
13068                             {
13069                                 xns : Roo.bootstrap,
13070                                 xtype : 'Button',
13071                                 style: 'max-height: 28px; ',
13072                                 size : 'sm',
13073                                 weight: 'danger',
13074                                 cls : 'col-2',
13075                                 fa : 'times',
13076                                 listeners : {
13077                                     click : function() {
13078                                         t.removeCard(data.id)
13079                                     }
13080                                 }
13081                             }
13082                         ]
13083                     }
13084                     
13085                 ] 
13086             }
13087             
13088         ];
13089         
13090         var cn = this.addxtype(
13091             {
13092                  
13093                 xns : Roo.bootstrap,
13094                 xtype : 'Card',
13095                 closeable : true,
13096                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13097                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13098                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13099                 data : data,
13100                 html : false,
13101                  
13102                 items : footer,
13103                 initEvents : function() {
13104                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13105                     var card = this;
13106                     this.imgEl = this.el.select('.card-img-top').first();
13107                     if (this.imgEl) {
13108                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13109                         this.imgEl.set({ 'pointer' : 'cursor' });
13110                                   
13111                     }
13112                     this.getCardFooter().addClass('p-1');
13113                     
13114                   
13115                 }
13116                 
13117             }
13118         );
13119         // dont' really need ot update items.
13120         // this.items.push(cn);
13121         this.fileCollection.add(cn);
13122         
13123         if (!data.srcfile) {
13124             this.updateInput();
13125             return;
13126         }
13127             
13128         var _t = this;
13129         var reader = new FileReader();
13130         reader.addEventListener("load", function() {  
13131             data.srcdata =  reader.result;
13132             _t.updateInput();
13133         });
13134         reader.readAsDataURL(data.srcfile);
13135         
13136         
13137         
13138     },
13139     removeCard : function(id)
13140     {
13141         
13142         var card  = this.fileCollection.get(id);
13143         card.data.is_deleted = 1;
13144         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13145         //this.fileCollection.remove(card);
13146         //this.items = this.items.filter(function(e) { return e != card });
13147         // dont' really need ot update items.
13148         card.el.dom.parentNode.removeChild(card.el.dom);
13149         this.updateInput();
13150
13151         
13152     },
13153     reset: function()
13154     {
13155         this.fileCollection.each(function(card) {
13156             if (card.el.dom && card.el.dom.parentNode) {
13157                 card.el.dom.parentNode.removeChild(card.el.dom);
13158             }
13159         });
13160         this.fileCollection.clear();
13161         this.updateInput();
13162     },
13163     
13164     updateInput : function()
13165     {
13166          var data = [];
13167         this.fileCollection.each(function(e) {
13168             data.push(e.data);
13169             
13170         });
13171         this.inputEl().dom.value = JSON.stringify(data);
13172         
13173         
13174         
13175     }
13176     
13177     
13178 });
13179
13180
13181 Roo.bootstrap.CardUploader.ID = -1;/*
13182  * Based on:
13183  * Ext JS Library 1.1.1
13184  * Copyright(c) 2006-2007, Ext JS, LLC.
13185  *
13186  * Originally Released Under LGPL - original licence link has changed is not relivant.
13187  *
13188  * Fork - LGPL
13189  * <script type="text/javascript">
13190  */
13191
13192
13193 /**
13194  * @class Roo.data.SortTypes
13195  * @singleton
13196  * Defines the default sorting (casting?) comparison functions used when sorting data.
13197  */
13198 Roo.data.SortTypes = {
13199     /**
13200      * Default sort that does nothing
13201      * @param {Mixed} s The value being converted
13202      * @return {Mixed} The comparison value
13203      */
13204     none : function(s){
13205         return s;
13206     },
13207     
13208     /**
13209      * The regular expression used to strip tags
13210      * @type {RegExp}
13211      * @property
13212      */
13213     stripTagsRE : /<\/?[^>]+>/gi,
13214     
13215     /**
13216      * Strips all HTML tags to sort on text only
13217      * @param {Mixed} s The value being converted
13218      * @return {String} The comparison value
13219      */
13220     asText : function(s){
13221         return String(s).replace(this.stripTagsRE, "");
13222     },
13223     
13224     /**
13225      * Strips all HTML tags to sort on text only - Case insensitive
13226      * @param {Mixed} s The value being converted
13227      * @return {String} The comparison value
13228      */
13229     asUCText : function(s){
13230         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13231     },
13232     
13233     /**
13234      * Case insensitive string
13235      * @param {Mixed} s The value being converted
13236      * @return {String} The comparison value
13237      */
13238     asUCString : function(s) {
13239         return String(s).toUpperCase();
13240     },
13241     
13242     /**
13243      * Date sorting
13244      * @param {Mixed} s The value being converted
13245      * @return {Number} The comparison value
13246      */
13247     asDate : function(s) {
13248         if(!s){
13249             return 0;
13250         }
13251         if(s instanceof Date){
13252             return s.getTime();
13253         }
13254         return Date.parse(String(s));
13255     },
13256     
13257     /**
13258      * Float sorting
13259      * @param {Mixed} s The value being converted
13260      * @return {Float} The comparison value
13261      */
13262     asFloat : function(s) {
13263         var val = parseFloat(String(s).replace(/,/g, ""));
13264         if(isNaN(val)) {
13265             val = 0;
13266         }
13267         return val;
13268     },
13269     
13270     /**
13271      * Integer sorting
13272      * @param {Mixed} s The value being converted
13273      * @return {Number} The comparison value
13274      */
13275     asInt : function(s) {
13276         var val = parseInt(String(s).replace(/,/g, ""));
13277         if(isNaN(val)) {
13278             val = 0;
13279         }
13280         return val;
13281     }
13282 };/*
13283  * Based on:
13284  * Ext JS Library 1.1.1
13285  * Copyright(c) 2006-2007, Ext JS, LLC.
13286  *
13287  * Originally Released Under LGPL - original licence link has changed is not relivant.
13288  *
13289  * Fork - LGPL
13290  * <script type="text/javascript">
13291  */
13292
13293 /**
13294 * @class Roo.data.Record
13295  * Instances of this class encapsulate both record <em>definition</em> information, and record
13296  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13297  * to access Records cached in an {@link Roo.data.Store} object.<br>
13298  * <p>
13299  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13300  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13301  * objects.<br>
13302  * <p>
13303  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13304  * @constructor
13305  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13306  * {@link #create}. The parameters are the same.
13307  * @param {Array} data An associative Array of data values keyed by the field name.
13308  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13309  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13310  * not specified an integer id is generated.
13311  */
13312 Roo.data.Record = function(data, id){
13313     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13314     this.data = data;
13315 };
13316
13317 /**
13318  * Generate a constructor for a specific record layout.
13319  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13320  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13321  * Each field definition object may contain the following properties: <ul>
13322  * <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,
13323  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13324  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13325  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13326  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13327  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13328  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13329  * this may be omitted.</p></li>
13330  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13331  * <ul><li>auto (Default, implies no conversion)</li>
13332  * <li>string</li>
13333  * <li>int</li>
13334  * <li>float</li>
13335  * <li>boolean</li>
13336  * <li>date</li></ul></p></li>
13337  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13338  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13339  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13340  * by the Reader into an object that will be stored in the Record. It is passed the
13341  * following parameters:<ul>
13342  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13343  * </ul></p></li>
13344  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13345  * </ul>
13346  * <br>usage:<br><pre><code>
13347 var TopicRecord = Roo.data.Record.create(
13348     {name: 'title', mapping: 'topic_title'},
13349     {name: 'author', mapping: 'username'},
13350     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13351     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13352     {name: 'lastPoster', mapping: 'user2'},
13353     {name: 'excerpt', mapping: 'post_text'}
13354 );
13355
13356 var myNewRecord = new TopicRecord({
13357     title: 'Do my job please',
13358     author: 'noobie',
13359     totalPosts: 1,
13360     lastPost: new Date(),
13361     lastPoster: 'Animal',
13362     excerpt: 'No way dude!'
13363 });
13364 myStore.add(myNewRecord);
13365 </code></pre>
13366  * @method create
13367  * @static
13368  */
13369 Roo.data.Record.create = function(o){
13370     var f = function(){
13371         f.superclass.constructor.apply(this, arguments);
13372     };
13373     Roo.extend(f, Roo.data.Record);
13374     var p = f.prototype;
13375     p.fields = new Roo.util.MixedCollection(false, function(field){
13376         return field.name;
13377     });
13378     for(var i = 0, len = o.length; i < len; i++){
13379         p.fields.add(new Roo.data.Field(o[i]));
13380     }
13381     f.getField = function(name){
13382         return p.fields.get(name);  
13383     };
13384     return f;
13385 };
13386
13387 Roo.data.Record.AUTO_ID = 1000;
13388 Roo.data.Record.EDIT = 'edit';
13389 Roo.data.Record.REJECT = 'reject';
13390 Roo.data.Record.COMMIT = 'commit';
13391
13392 Roo.data.Record.prototype = {
13393     /**
13394      * Readonly flag - true if this record has been modified.
13395      * @type Boolean
13396      */
13397     dirty : false,
13398     editing : false,
13399     error: null,
13400     modified: null,
13401
13402     // private
13403     join : function(store){
13404         this.store = store;
13405     },
13406
13407     /**
13408      * Set the named field to the specified value.
13409      * @param {String} name The name of the field to set.
13410      * @param {Object} value The value to set the field to.
13411      */
13412     set : function(name, value){
13413         if(this.data[name] == value){
13414             return;
13415         }
13416         this.dirty = true;
13417         if(!this.modified){
13418             this.modified = {};
13419         }
13420         if(typeof this.modified[name] == 'undefined'){
13421             this.modified[name] = this.data[name];
13422         }
13423         this.data[name] = value;
13424         if(!this.editing && this.store){
13425             this.store.afterEdit(this);
13426         }       
13427     },
13428
13429     /**
13430      * Get the value of the named field.
13431      * @param {String} name The name of the field to get the value of.
13432      * @return {Object} The value of the field.
13433      */
13434     get : function(name){
13435         return this.data[name]; 
13436     },
13437
13438     // private
13439     beginEdit : function(){
13440         this.editing = true;
13441         this.modified = {}; 
13442     },
13443
13444     // private
13445     cancelEdit : function(){
13446         this.editing = false;
13447         delete this.modified;
13448     },
13449
13450     // private
13451     endEdit : function(){
13452         this.editing = false;
13453         if(this.dirty && this.store){
13454             this.store.afterEdit(this);
13455         }
13456     },
13457
13458     /**
13459      * Usually called by the {@link Roo.data.Store} which owns the Record.
13460      * Rejects all changes made to the Record since either creation, or the last commit operation.
13461      * Modified fields are reverted to their original values.
13462      * <p>
13463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464      * of reject operations.
13465      */
13466     reject : function(){
13467         var m = this.modified;
13468         for(var n in m){
13469             if(typeof m[n] != "function"){
13470                 this.data[n] = m[n];
13471             }
13472         }
13473         this.dirty = false;
13474         delete this.modified;
13475         this.editing = false;
13476         if(this.store){
13477             this.store.afterReject(this);
13478         }
13479     },
13480
13481     /**
13482      * Usually called by the {@link Roo.data.Store} which owns the Record.
13483      * Commits all changes made to the Record since either creation, or the last commit operation.
13484      * <p>
13485      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13486      * of commit operations.
13487      */
13488     commit : function(){
13489         this.dirty = false;
13490         delete this.modified;
13491         this.editing = false;
13492         if(this.store){
13493             this.store.afterCommit(this);
13494         }
13495     },
13496
13497     // private
13498     hasError : function(){
13499         return this.error != null;
13500     },
13501
13502     // private
13503     clearError : function(){
13504         this.error = null;
13505     },
13506
13507     /**
13508      * Creates a copy of this record.
13509      * @param {String} id (optional) A new record id if you don't want to use this record's id
13510      * @return {Record}
13511      */
13512     copy : function(newId) {
13513         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13514     }
13515 };/*
13516  * Based on:
13517  * Ext JS Library 1.1.1
13518  * Copyright(c) 2006-2007, Ext JS, LLC.
13519  *
13520  * Originally Released Under LGPL - original licence link has changed is not relivant.
13521  *
13522  * Fork - LGPL
13523  * <script type="text/javascript">
13524  */
13525
13526
13527
13528 /**
13529  * @class Roo.data.Store
13530  * @extends Roo.util.Observable
13531  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13532  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13533  * <p>
13534  * 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
13535  * has no knowledge of the format of the data returned by the Proxy.<br>
13536  * <p>
13537  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13538  * instances from the data object. These records are cached and made available through accessor functions.
13539  * @constructor
13540  * Creates a new Store.
13541  * @param {Object} config A config object containing the objects needed for the Store to access data,
13542  * and read the data into Records.
13543  */
13544 Roo.data.Store = function(config){
13545     this.data = new Roo.util.MixedCollection(false);
13546     this.data.getKey = function(o){
13547         return o.id;
13548     };
13549     this.baseParams = {};
13550     // private
13551     this.paramNames = {
13552         "start" : "start",
13553         "limit" : "limit",
13554         "sort" : "sort",
13555         "dir" : "dir",
13556         "multisort" : "_multisort"
13557     };
13558
13559     if(config && config.data){
13560         this.inlineData = config.data;
13561         delete config.data;
13562     }
13563
13564     Roo.apply(this, config);
13565     
13566     if(this.reader){ // reader passed
13567         this.reader = Roo.factory(this.reader, Roo.data);
13568         this.reader.xmodule = this.xmodule || false;
13569         if(!this.recordType){
13570             this.recordType = this.reader.recordType;
13571         }
13572         if(this.reader.onMetaChange){
13573             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13574         }
13575     }
13576
13577     if(this.recordType){
13578         this.fields = this.recordType.prototype.fields;
13579     }
13580     this.modified = [];
13581
13582     this.addEvents({
13583         /**
13584          * @event datachanged
13585          * Fires when the data cache has changed, and a widget which is using this Store
13586          * as a Record cache should refresh its view.
13587          * @param {Store} this
13588          */
13589         datachanged : true,
13590         /**
13591          * @event metachange
13592          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13593          * @param {Store} this
13594          * @param {Object} meta The JSON metadata
13595          */
13596         metachange : true,
13597         /**
13598          * @event add
13599          * Fires when Records have been added to the Store
13600          * @param {Store} this
13601          * @param {Roo.data.Record[]} records The array of Records added
13602          * @param {Number} index The index at which the record(s) were added
13603          */
13604         add : true,
13605         /**
13606          * @event remove
13607          * Fires when a Record has been removed from the Store
13608          * @param {Store} this
13609          * @param {Roo.data.Record} record The Record that was removed
13610          * @param {Number} index The index at which the record was removed
13611          */
13612         remove : true,
13613         /**
13614          * @event update
13615          * Fires when a Record has been updated
13616          * @param {Store} this
13617          * @param {Roo.data.Record} record The Record that was updated
13618          * @param {String} operation The update operation being performed.  Value may be one of:
13619          * <pre><code>
13620  Roo.data.Record.EDIT
13621  Roo.data.Record.REJECT
13622  Roo.data.Record.COMMIT
13623          * </code></pre>
13624          */
13625         update : true,
13626         /**
13627          * @event clear
13628          * Fires when the data cache has been cleared.
13629          * @param {Store} this
13630          */
13631         clear : true,
13632         /**
13633          * @event beforeload
13634          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13635          * the load action will be canceled.
13636          * @param {Store} this
13637          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13638          */
13639         beforeload : true,
13640         /**
13641          * @event beforeloadadd
13642          * Fires after a new set of Records has been loaded.
13643          * @param {Store} this
13644          * @param {Roo.data.Record[]} records The Records that were loaded
13645          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13646          */
13647         beforeloadadd : true,
13648         /**
13649          * @event load
13650          * Fires after a new set of Records has been loaded, before they are added to the store.
13651          * @param {Store} this
13652          * @param {Roo.data.Record[]} records The Records that were loaded
13653          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13654          * @params {Object} return from reader
13655          */
13656         load : true,
13657         /**
13658          * @event loadexception
13659          * Fires if an exception occurs in the Proxy during loading.
13660          * Called with the signature of the Proxy's "loadexception" event.
13661          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13662          * 
13663          * @param {Proxy} 
13664          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13665          * @param {Object} load options 
13666          * @param {Object} jsonData from your request (normally this contains the Exception)
13667          */
13668         loadexception : true
13669     });
13670     
13671     if(this.proxy){
13672         this.proxy = Roo.factory(this.proxy, Roo.data);
13673         this.proxy.xmodule = this.xmodule || false;
13674         this.relayEvents(this.proxy,  ["loadexception"]);
13675     }
13676     this.sortToggle = {};
13677     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13678
13679     Roo.data.Store.superclass.constructor.call(this);
13680
13681     if(this.inlineData){
13682         this.loadData(this.inlineData);
13683         delete this.inlineData;
13684     }
13685 };
13686
13687 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13688      /**
13689     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13690     * without a remote query - used by combo/forms at present.
13691     */
13692     
13693     /**
13694     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13695     */
13696     /**
13697     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13698     */
13699     /**
13700     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13701     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13702     */
13703     /**
13704     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13705     * on any HTTP request
13706     */
13707     /**
13708     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13709     */
13710     /**
13711     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13712     */
13713     multiSort: false,
13714     /**
13715     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13716     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13717     */
13718     remoteSort : false,
13719
13720     /**
13721     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13722      * loaded or when a record is removed. (defaults to false).
13723     */
13724     pruneModifiedRecords : false,
13725
13726     // private
13727     lastOptions : null,
13728
13729     /**
13730      * Add Records to the Store and fires the add event.
13731      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13732      */
13733     add : function(records){
13734         records = [].concat(records);
13735         for(var i = 0, len = records.length; i < len; i++){
13736             records[i].join(this);
13737         }
13738         var index = this.data.length;
13739         this.data.addAll(records);
13740         this.fireEvent("add", this, records, index);
13741     },
13742
13743     /**
13744      * Remove a Record from the Store and fires the remove event.
13745      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13746      */
13747     remove : function(record){
13748         var index = this.data.indexOf(record);
13749         this.data.removeAt(index);
13750  
13751         if(this.pruneModifiedRecords){
13752             this.modified.remove(record);
13753         }
13754         this.fireEvent("remove", this, record, index);
13755     },
13756
13757     /**
13758      * Remove all Records from the Store and fires the clear event.
13759      */
13760     removeAll : function(){
13761         this.data.clear();
13762         if(this.pruneModifiedRecords){
13763             this.modified = [];
13764         }
13765         this.fireEvent("clear", this);
13766     },
13767
13768     /**
13769      * Inserts Records to the Store at the given index and fires the add event.
13770      * @param {Number} index The start index at which to insert the passed Records.
13771      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13772      */
13773     insert : function(index, records){
13774         records = [].concat(records);
13775         for(var i = 0, len = records.length; i < len; i++){
13776             this.data.insert(index, records[i]);
13777             records[i].join(this);
13778         }
13779         this.fireEvent("add", this, records, index);
13780     },
13781
13782     /**
13783      * Get the index within the cache of the passed Record.
13784      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13785      * @return {Number} The index of the passed Record. Returns -1 if not found.
13786      */
13787     indexOf : function(record){
13788         return this.data.indexOf(record);
13789     },
13790
13791     /**
13792      * Get the index within the cache of the Record with the passed id.
13793      * @param {String} id The id of the Record to find.
13794      * @return {Number} The index of the Record. Returns -1 if not found.
13795      */
13796     indexOfId : function(id){
13797         return this.data.indexOfKey(id);
13798     },
13799
13800     /**
13801      * Get the Record with the specified id.
13802      * @param {String} id The id of the Record to find.
13803      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13804      */
13805     getById : function(id){
13806         return this.data.key(id);
13807     },
13808
13809     /**
13810      * Get the Record at the specified index.
13811      * @param {Number} index The index of the Record to find.
13812      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13813      */
13814     getAt : function(index){
13815         return this.data.itemAt(index);
13816     },
13817
13818     /**
13819      * Returns a range of Records between specified indices.
13820      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13821      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13822      * @return {Roo.data.Record[]} An array of Records
13823      */
13824     getRange : function(start, end){
13825         return this.data.getRange(start, end);
13826     },
13827
13828     // private
13829     storeOptions : function(o){
13830         o = Roo.apply({}, o);
13831         delete o.callback;
13832         delete o.scope;
13833         this.lastOptions = o;
13834     },
13835
13836     /**
13837      * Loads the Record cache from the configured Proxy using the configured Reader.
13838      * <p>
13839      * If using remote paging, then the first load call must specify the <em>start</em>
13840      * and <em>limit</em> properties in the options.params property to establish the initial
13841      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13842      * <p>
13843      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13844      * and this call will return before the new data has been loaded. Perform any post-processing
13845      * in a callback function, or in a "load" event handler.</strong>
13846      * <p>
13847      * @param {Object} options An object containing properties which control loading options:<ul>
13848      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13849      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13850      * passed the following arguments:<ul>
13851      * <li>r : Roo.data.Record[]</li>
13852      * <li>options: Options object from the load call</li>
13853      * <li>success: Boolean success indicator</li></ul></li>
13854      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13855      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13856      * </ul>
13857      */
13858     load : function(options){
13859         options = options || {};
13860         if(this.fireEvent("beforeload", this, options) !== false){
13861             this.storeOptions(options);
13862             var p = Roo.apply(options.params || {}, this.baseParams);
13863             // if meta was not loaded from remote source.. try requesting it.
13864             if (!this.reader.metaFromRemote) {
13865                 p._requestMeta = 1;
13866             }
13867             if(this.sortInfo && this.remoteSort){
13868                 var pn = this.paramNames;
13869                 p[pn["sort"]] = this.sortInfo.field;
13870                 p[pn["dir"]] = this.sortInfo.direction;
13871             }
13872             if (this.multiSort) {
13873                 var pn = this.paramNames;
13874                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13875             }
13876             
13877             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13878         }
13879     },
13880
13881     /**
13882      * Reloads the Record cache from the configured Proxy using the configured Reader and
13883      * the options from the last load operation performed.
13884      * @param {Object} options (optional) An object containing properties which may override the options
13885      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13886      * the most recently used options are reused).
13887      */
13888     reload : function(options){
13889         this.load(Roo.applyIf(options||{}, this.lastOptions));
13890     },
13891
13892     // private
13893     // Called as a callback by the Reader during a load operation.
13894     loadRecords : function(o, options, success){
13895         if(!o || success === false){
13896             if(success !== false){
13897                 this.fireEvent("load", this, [], options, o);
13898             }
13899             if(options.callback){
13900                 options.callback.call(options.scope || this, [], options, false);
13901             }
13902             return;
13903         }
13904         // if data returned failure - throw an exception.
13905         if (o.success === false) {
13906             // show a message if no listener is registered.
13907             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13908                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13909             }
13910             // loadmask wil be hooked into this..
13911             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13912             return;
13913         }
13914         var r = o.records, t = o.totalRecords || r.length;
13915         
13916         this.fireEvent("beforeloadadd", this, r, options, o);
13917         
13918         if(!options || options.add !== true){
13919             if(this.pruneModifiedRecords){
13920                 this.modified = [];
13921             }
13922             for(var i = 0, len = r.length; i < len; i++){
13923                 r[i].join(this);
13924             }
13925             if(this.snapshot){
13926                 this.data = this.snapshot;
13927                 delete this.snapshot;
13928             }
13929             this.data.clear();
13930             this.data.addAll(r);
13931             this.totalLength = t;
13932             this.applySort();
13933             this.fireEvent("datachanged", this);
13934         }else{
13935             this.totalLength = Math.max(t, this.data.length+r.length);
13936             this.add(r);
13937         }
13938         
13939         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13940                 
13941             var e = new Roo.data.Record({});
13942
13943             e.set(this.parent.displayField, this.parent.emptyTitle);
13944             e.set(this.parent.valueField, '');
13945
13946             this.insert(0, e);
13947         }
13948             
13949         this.fireEvent("load", this, r, options, o);
13950         if(options.callback){
13951             options.callback.call(options.scope || this, r, options, true);
13952         }
13953     },
13954
13955
13956     /**
13957      * Loads data from a passed data block. A Reader which understands the format of the data
13958      * must have been configured in the constructor.
13959      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13960      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13961      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13962      */
13963     loadData : function(o, append){
13964         var r = this.reader.readRecords(o);
13965         this.loadRecords(r, {add: append}, true);
13966     },
13967     
13968      /**
13969      * using 'cn' the nested child reader read the child array into it's child stores.
13970      * @param {Object} rec The record with a 'children array
13971      */
13972     loadDataFromChildren : function(rec)
13973     {
13974         this.loadData(this.reader.toLoadData(rec));
13975     },
13976     
13977
13978     /**
13979      * Gets the number of cached records.
13980      * <p>
13981      * <em>If using paging, this may not be the total size of the dataset. If the data object
13982      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13983      * the data set size</em>
13984      */
13985     getCount : function(){
13986         return this.data.length || 0;
13987     },
13988
13989     /**
13990      * Gets the total number of records in the dataset as returned by the server.
13991      * <p>
13992      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13993      * the dataset size</em>
13994      */
13995     getTotalCount : function(){
13996         return this.totalLength || 0;
13997     },
13998
13999     /**
14000      * Returns the sort state of the Store as an object with two properties:
14001      * <pre><code>
14002  field {String} The name of the field by which the Records are sorted
14003  direction {String} The sort order, "ASC" or "DESC"
14004      * </code></pre>
14005      */
14006     getSortState : function(){
14007         return this.sortInfo;
14008     },
14009
14010     // private
14011     applySort : function(){
14012         if(this.sortInfo && !this.remoteSort){
14013             var s = this.sortInfo, f = s.field;
14014             var st = this.fields.get(f).sortType;
14015             var fn = function(r1, r2){
14016                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14017                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14018             };
14019             this.data.sort(s.direction, fn);
14020             if(this.snapshot && this.snapshot != this.data){
14021                 this.snapshot.sort(s.direction, fn);
14022             }
14023         }
14024     },
14025
14026     /**
14027      * Sets the default sort column and order to be used by the next load operation.
14028      * @param {String} fieldName The name of the field to sort by.
14029      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14030      */
14031     setDefaultSort : function(field, dir){
14032         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14033     },
14034
14035     /**
14036      * Sort the Records.
14037      * If remote sorting is used, the sort is performed on the server, and the cache is
14038      * reloaded. If local sorting is used, the cache is sorted internally.
14039      * @param {String} fieldName The name of the field to sort by.
14040      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14041      */
14042     sort : function(fieldName, dir){
14043         var f = this.fields.get(fieldName);
14044         if(!dir){
14045             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14046             
14047             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14048                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14049             }else{
14050                 dir = f.sortDir;
14051             }
14052         }
14053         this.sortToggle[f.name] = dir;
14054         this.sortInfo = {field: f.name, direction: dir};
14055         if(!this.remoteSort){
14056             this.applySort();
14057             this.fireEvent("datachanged", this);
14058         }else{
14059             this.load(this.lastOptions);
14060         }
14061     },
14062
14063     /**
14064      * Calls the specified function for each of the Records in the cache.
14065      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14066      * Returning <em>false</em> aborts and exits the iteration.
14067      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14068      */
14069     each : function(fn, scope){
14070         this.data.each(fn, scope);
14071     },
14072
14073     /**
14074      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14075      * (e.g., during paging).
14076      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14077      */
14078     getModifiedRecords : function(){
14079         return this.modified;
14080     },
14081
14082     // private
14083     createFilterFn : function(property, value, anyMatch){
14084         if(!value.exec){ // not a regex
14085             value = String(value);
14086             if(value.length == 0){
14087                 return false;
14088             }
14089             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14090         }
14091         return function(r){
14092             return value.test(r.data[property]);
14093         };
14094     },
14095
14096     /**
14097      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14098      * @param {String} property A field on your records
14099      * @param {Number} start The record index to start at (defaults to 0)
14100      * @param {Number} end The last record index to include (defaults to length - 1)
14101      * @return {Number} The sum
14102      */
14103     sum : function(property, start, end){
14104         var rs = this.data.items, v = 0;
14105         start = start || 0;
14106         end = (end || end === 0) ? end : rs.length-1;
14107
14108         for(var i = start; i <= end; i++){
14109             v += (rs[i].data[property] || 0);
14110         }
14111         return v;
14112     },
14113
14114     /**
14115      * Filter the records by a specified property.
14116      * @param {String} field A field on your records
14117      * @param {String/RegExp} value Either a string that the field
14118      * should start with or a RegExp to test against the field
14119      * @param {Boolean} anyMatch True to match any part not just the beginning
14120      */
14121     filter : function(property, value, anyMatch){
14122         var fn = this.createFilterFn(property, value, anyMatch);
14123         return fn ? this.filterBy(fn) : this.clearFilter();
14124     },
14125
14126     /**
14127      * Filter by a function. The specified function will be called with each
14128      * record in this data source. If the function returns true the record is included,
14129      * otherwise it is filtered.
14130      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14131      * @param {Object} scope (optional) The scope of the function (defaults to this)
14132      */
14133     filterBy : function(fn, scope){
14134         this.snapshot = this.snapshot || this.data;
14135         this.data = this.queryBy(fn, scope||this);
14136         this.fireEvent("datachanged", this);
14137     },
14138
14139     /**
14140      * Query the records by a specified property.
14141      * @param {String} field A field on your records
14142      * @param {String/RegExp} value Either a string that the field
14143      * should start with or a RegExp to test against the field
14144      * @param {Boolean} anyMatch True to match any part not just the beginning
14145      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14146      */
14147     query : function(property, value, anyMatch){
14148         var fn = this.createFilterFn(property, value, anyMatch);
14149         return fn ? this.queryBy(fn) : this.data.clone();
14150     },
14151
14152     /**
14153      * Query by a function. The specified function will be called with each
14154      * record in this data source. If the function returns true the record is included
14155      * in the results.
14156      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14157      * @param {Object} scope (optional) The scope of the function (defaults to this)
14158       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14159      **/
14160     queryBy : function(fn, scope){
14161         var data = this.snapshot || this.data;
14162         return data.filterBy(fn, scope||this);
14163     },
14164
14165     /**
14166      * Collects unique values for a particular dataIndex from this store.
14167      * @param {String} dataIndex The property to collect
14168      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14169      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14170      * @return {Array} An array of the unique values
14171      **/
14172     collect : function(dataIndex, allowNull, bypassFilter){
14173         var d = (bypassFilter === true && this.snapshot) ?
14174                 this.snapshot.items : this.data.items;
14175         var v, sv, r = [], l = {};
14176         for(var i = 0, len = d.length; i < len; i++){
14177             v = d[i].data[dataIndex];
14178             sv = String(v);
14179             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14180                 l[sv] = true;
14181                 r[r.length] = v;
14182             }
14183         }
14184         return r;
14185     },
14186
14187     /**
14188      * Revert to a view of the Record cache with no filtering applied.
14189      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14190      */
14191     clearFilter : function(suppressEvent){
14192         if(this.snapshot && this.snapshot != this.data){
14193             this.data = this.snapshot;
14194             delete this.snapshot;
14195             if(suppressEvent !== true){
14196                 this.fireEvent("datachanged", this);
14197             }
14198         }
14199     },
14200
14201     // private
14202     afterEdit : function(record){
14203         if(this.modified.indexOf(record) == -1){
14204             this.modified.push(record);
14205         }
14206         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14207     },
14208     
14209     // private
14210     afterReject : function(record){
14211         this.modified.remove(record);
14212         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14213     },
14214
14215     // private
14216     afterCommit : function(record){
14217         this.modified.remove(record);
14218         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14219     },
14220
14221     /**
14222      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14223      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14224      */
14225     commitChanges : function(){
14226         var m = this.modified.slice(0);
14227         this.modified = [];
14228         for(var i = 0, len = m.length; i < len; i++){
14229             m[i].commit();
14230         }
14231     },
14232
14233     /**
14234      * Cancel outstanding changes on all changed records.
14235      */
14236     rejectChanges : function(){
14237         var m = this.modified.slice(0);
14238         this.modified = [];
14239         for(var i = 0, len = m.length; i < len; i++){
14240             m[i].reject();
14241         }
14242     },
14243
14244     onMetaChange : function(meta, rtype, o){
14245         this.recordType = rtype;
14246         this.fields = rtype.prototype.fields;
14247         delete this.snapshot;
14248         this.sortInfo = meta.sortInfo || this.sortInfo;
14249         this.modified = [];
14250         this.fireEvent('metachange', this, this.reader.meta);
14251     },
14252     
14253     moveIndex : function(data, type)
14254     {
14255         var index = this.indexOf(data);
14256         
14257         var newIndex = index + type;
14258         
14259         this.remove(data);
14260         
14261         this.insert(newIndex, data);
14262         
14263     }
14264 });/*
14265  * Based on:
14266  * Ext JS Library 1.1.1
14267  * Copyright(c) 2006-2007, Ext JS, LLC.
14268  *
14269  * Originally Released Under LGPL - original licence link has changed is not relivant.
14270  *
14271  * Fork - LGPL
14272  * <script type="text/javascript">
14273  */
14274
14275 /**
14276  * @class Roo.data.SimpleStore
14277  * @extends Roo.data.Store
14278  * Small helper class to make creating Stores from Array data easier.
14279  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14280  * @cfg {Array} fields An array of field definition objects, or field name strings.
14281  * @cfg {Object} an existing reader (eg. copied from another store)
14282  * @cfg {Array} data The multi-dimensional array of data
14283  * @constructor
14284  * @param {Object} config
14285  */
14286 Roo.data.SimpleStore = function(config)
14287 {
14288     Roo.data.SimpleStore.superclass.constructor.call(this, {
14289         isLocal : true,
14290         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14291                 id: config.id
14292             },
14293             Roo.data.Record.create(config.fields)
14294         ),
14295         proxy : new Roo.data.MemoryProxy(config.data)
14296     });
14297     this.load();
14298 };
14299 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14300  * Based on:
14301  * Ext JS Library 1.1.1
14302  * Copyright(c) 2006-2007, Ext JS, LLC.
14303  *
14304  * Originally Released Under LGPL - original licence link has changed is not relivant.
14305  *
14306  * Fork - LGPL
14307  * <script type="text/javascript">
14308  */
14309
14310 /**
14311 /**
14312  * @extends Roo.data.Store
14313  * @class Roo.data.JsonStore
14314  * Small helper class to make creating Stores for JSON data easier. <br/>
14315 <pre><code>
14316 var store = new Roo.data.JsonStore({
14317     url: 'get-images.php',
14318     root: 'images',
14319     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14320 });
14321 </code></pre>
14322  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14323  * JsonReader and HttpProxy (unless inline data is provided).</b>
14324  * @cfg {Array} fields An array of field definition objects, or field name strings.
14325  * @constructor
14326  * @param {Object} config
14327  */
14328 Roo.data.JsonStore = function(c){
14329     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14330         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14331         reader: new Roo.data.JsonReader(c, c.fields)
14332     }));
14333 };
14334 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14335  * Based on:
14336  * Ext JS Library 1.1.1
14337  * Copyright(c) 2006-2007, Ext JS, LLC.
14338  *
14339  * Originally Released Under LGPL - original licence link has changed is not relivant.
14340  *
14341  * Fork - LGPL
14342  * <script type="text/javascript">
14343  */
14344
14345  
14346 Roo.data.Field = function(config){
14347     if(typeof config == "string"){
14348         config = {name: config};
14349     }
14350     Roo.apply(this, config);
14351     
14352     if(!this.type){
14353         this.type = "auto";
14354     }
14355     
14356     var st = Roo.data.SortTypes;
14357     // named sortTypes are supported, here we look them up
14358     if(typeof this.sortType == "string"){
14359         this.sortType = st[this.sortType];
14360     }
14361     
14362     // set default sortType for strings and dates
14363     if(!this.sortType){
14364         switch(this.type){
14365             case "string":
14366                 this.sortType = st.asUCString;
14367                 break;
14368             case "date":
14369                 this.sortType = st.asDate;
14370                 break;
14371             default:
14372                 this.sortType = st.none;
14373         }
14374     }
14375
14376     // define once
14377     var stripRe = /[\$,%]/g;
14378
14379     // prebuilt conversion function for this field, instead of
14380     // switching every time we're reading a value
14381     if(!this.convert){
14382         var cv, dateFormat = this.dateFormat;
14383         switch(this.type){
14384             case "":
14385             case "auto":
14386             case undefined:
14387                 cv = function(v){ return v; };
14388                 break;
14389             case "string":
14390                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14391                 break;
14392             case "int":
14393                 cv = function(v){
14394                     return v !== undefined && v !== null && v !== '' ?
14395                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14396                     };
14397                 break;
14398             case "float":
14399                 cv = function(v){
14400                     return v !== undefined && v !== null && v !== '' ?
14401                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14402                     };
14403                 break;
14404             case "bool":
14405             case "boolean":
14406                 cv = function(v){ return v === true || v === "true" || v == 1; };
14407                 break;
14408             case "date":
14409                 cv = function(v){
14410                     if(!v){
14411                         return '';
14412                     }
14413                     if(v instanceof Date){
14414                         return v;
14415                     }
14416                     if(dateFormat){
14417                         if(dateFormat == "timestamp"){
14418                             return new Date(v*1000);
14419                         }
14420                         return Date.parseDate(v, dateFormat);
14421                     }
14422                     var parsed = Date.parse(v);
14423                     return parsed ? new Date(parsed) : null;
14424                 };
14425              break;
14426             
14427         }
14428         this.convert = cv;
14429     }
14430 };
14431
14432 Roo.data.Field.prototype = {
14433     dateFormat: null,
14434     defaultValue: "",
14435     mapping: null,
14436     sortType : null,
14437     sortDir : "ASC"
14438 };/*
14439  * Based on:
14440  * Ext JS Library 1.1.1
14441  * Copyright(c) 2006-2007, Ext JS, LLC.
14442  *
14443  * Originally Released Under LGPL - original licence link has changed is not relivant.
14444  *
14445  * Fork - LGPL
14446  * <script type="text/javascript">
14447  */
14448  
14449 // Base class for reading structured data from a data source.  This class is intended to be
14450 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14451
14452 /**
14453  * @class Roo.data.DataReader
14454  * Base class for reading structured data from a data source.  This class is intended to be
14455  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14456  */
14457
14458 Roo.data.DataReader = function(meta, recordType){
14459     
14460     this.meta = meta;
14461     
14462     this.recordType = recordType instanceof Array ? 
14463         Roo.data.Record.create(recordType) : recordType;
14464 };
14465
14466 Roo.data.DataReader.prototype = {
14467     
14468     
14469     readerType : 'Data',
14470      /**
14471      * Create an empty record
14472      * @param {Object} data (optional) - overlay some values
14473      * @return {Roo.data.Record} record created.
14474      */
14475     newRow :  function(d) {
14476         var da =  {};
14477         this.recordType.prototype.fields.each(function(c) {
14478             switch( c.type) {
14479                 case 'int' : da[c.name] = 0; break;
14480                 case 'date' : da[c.name] = new Date(); break;
14481                 case 'float' : da[c.name] = 0.0; break;
14482                 case 'boolean' : da[c.name] = false; break;
14483                 default : da[c.name] = ""; break;
14484             }
14485             
14486         });
14487         return new this.recordType(Roo.apply(da, d));
14488     }
14489     
14490     
14491 };/*
14492  * Based on:
14493  * Ext JS Library 1.1.1
14494  * Copyright(c) 2006-2007, Ext JS, LLC.
14495  *
14496  * Originally Released Under LGPL - original licence link has changed is not relivant.
14497  *
14498  * Fork - LGPL
14499  * <script type="text/javascript">
14500  */
14501
14502 /**
14503  * @class Roo.data.DataProxy
14504  * @extends Roo.data.Observable
14505  * This class is an abstract base class for implementations which provide retrieval of
14506  * unformatted data objects.<br>
14507  * <p>
14508  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14509  * (of the appropriate type which knows how to parse the data object) to provide a block of
14510  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14511  * <p>
14512  * Custom implementations must implement the load method as described in
14513  * {@link Roo.data.HttpProxy#load}.
14514  */
14515 Roo.data.DataProxy = function(){
14516     this.addEvents({
14517         /**
14518          * @event beforeload
14519          * Fires before a network request is made to retrieve a data object.
14520          * @param {Object} This DataProxy object.
14521          * @param {Object} params The params parameter to the load function.
14522          */
14523         beforeload : true,
14524         /**
14525          * @event load
14526          * Fires before the load method's callback is called.
14527          * @param {Object} This DataProxy object.
14528          * @param {Object} o The data object.
14529          * @param {Object} arg The callback argument object passed to the load function.
14530          */
14531         load : true,
14532         /**
14533          * @event loadexception
14534          * Fires if an Exception occurs during data retrieval.
14535          * @param {Object} This DataProxy object.
14536          * @param {Object} o The data object.
14537          * @param {Object} arg The callback argument object passed to the load function.
14538          * @param {Object} e The Exception.
14539          */
14540         loadexception : true
14541     });
14542     Roo.data.DataProxy.superclass.constructor.call(this);
14543 };
14544
14545 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14546
14547     /**
14548      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14549      */
14550 /*
14551  * Based on:
14552  * Ext JS Library 1.1.1
14553  * Copyright(c) 2006-2007, Ext JS, LLC.
14554  *
14555  * Originally Released Under LGPL - original licence link has changed is not relivant.
14556  *
14557  * Fork - LGPL
14558  * <script type="text/javascript">
14559  */
14560 /**
14561  * @class Roo.data.MemoryProxy
14562  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14563  * to the Reader when its load method is called.
14564  * @constructor
14565  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14566  */
14567 Roo.data.MemoryProxy = function(data){
14568     if (data.data) {
14569         data = data.data;
14570     }
14571     Roo.data.MemoryProxy.superclass.constructor.call(this);
14572     this.data = data;
14573 };
14574
14575 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14576     
14577     /**
14578      * Load data from the requested source (in this case an in-memory
14579      * data object passed to the constructor), read the data object into
14580      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14581      * process that block using the passed callback.
14582      * @param {Object} params This parameter is not used by the MemoryProxy class.
14583      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14584      * object into a block of Roo.data.Records.
14585      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14586      * The function must be passed <ul>
14587      * <li>The Record block object</li>
14588      * <li>The "arg" argument from the load function</li>
14589      * <li>A boolean success indicator</li>
14590      * </ul>
14591      * @param {Object} scope The scope in which to call the callback
14592      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14593      */
14594     load : function(params, reader, callback, scope, arg){
14595         params = params || {};
14596         var result;
14597         try {
14598             result = reader.readRecords(params.data ? params.data :this.data);
14599         }catch(e){
14600             this.fireEvent("loadexception", this, arg, null, e);
14601             callback.call(scope, null, arg, false);
14602             return;
14603         }
14604         callback.call(scope, result, arg, true);
14605     },
14606     
14607     // private
14608     update : function(params, records){
14609         
14610     }
14611 });/*
14612  * Based on:
14613  * Ext JS Library 1.1.1
14614  * Copyright(c) 2006-2007, Ext JS, LLC.
14615  *
14616  * Originally Released Under LGPL - original licence link has changed is not relivant.
14617  *
14618  * Fork - LGPL
14619  * <script type="text/javascript">
14620  */
14621 /**
14622  * @class Roo.data.HttpProxy
14623  * @extends Roo.data.DataProxy
14624  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14625  * configured to reference a certain URL.<br><br>
14626  * <p>
14627  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14628  * from which the running page was served.<br><br>
14629  * <p>
14630  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14631  * <p>
14632  * Be aware that to enable the browser to parse an XML document, the server must set
14633  * the Content-Type header in the HTTP response to "text/xml".
14634  * @constructor
14635  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14636  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14637  * will be used to make the request.
14638  */
14639 Roo.data.HttpProxy = function(conn){
14640     Roo.data.HttpProxy.superclass.constructor.call(this);
14641     // is conn a conn config or a real conn?
14642     this.conn = conn;
14643     this.useAjax = !conn || !conn.events;
14644   
14645 };
14646
14647 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14648     // thse are take from connection...
14649     
14650     /**
14651      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14652      */
14653     /**
14654      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14655      * extra parameters to each request made by this object. (defaults to undefined)
14656      */
14657     /**
14658      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14659      *  to each request made by this object. (defaults to undefined)
14660      */
14661     /**
14662      * @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)
14663      */
14664     /**
14665      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14666      */
14667      /**
14668      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14669      * @type Boolean
14670      */
14671   
14672
14673     /**
14674      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14675      * @type Boolean
14676      */
14677     /**
14678      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14679      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14680      * a finer-grained basis than the DataProxy events.
14681      */
14682     getConnection : function(){
14683         return this.useAjax ? Roo.Ajax : this.conn;
14684     },
14685
14686     /**
14687      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14688      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14689      * process that block using the passed callback.
14690      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14691      * for the request to the remote server.
14692      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14693      * object into a block of Roo.data.Records.
14694      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14695      * The function must be passed <ul>
14696      * <li>The Record block object</li>
14697      * <li>The "arg" argument from the load function</li>
14698      * <li>A boolean success indicator</li>
14699      * </ul>
14700      * @param {Object} scope The scope in which to call the callback
14701      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14702      */
14703     load : function(params, reader, callback, scope, arg){
14704         if(this.fireEvent("beforeload", this, params) !== false){
14705             var  o = {
14706                 params : params || {},
14707                 request: {
14708                     callback : callback,
14709                     scope : scope,
14710                     arg : arg
14711                 },
14712                 reader: reader,
14713                 callback : this.loadResponse,
14714                 scope: this
14715             };
14716             if(this.useAjax){
14717                 Roo.applyIf(o, this.conn);
14718                 if(this.activeRequest){
14719                     Roo.Ajax.abort(this.activeRequest);
14720                 }
14721                 this.activeRequest = Roo.Ajax.request(o);
14722             }else{
14723                 this.conn.request(o);
14724             }
14725         }else{
14726             callback.call(scope||this, null, arg, false);
14727         }
14728     },
14729
14730     // private
14731     loadResponse : function(o, success, response){
14732         delete this.activeRequest;
14733         if(!success){
14734             this.fireEvent("loadexception", this, o, response);
14735             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14736             return;
14737         }
14738         var result;
14739         try {
14740             result = o.reader.read(response);
14741         }catch(e){
14742             this.fireEvent("loadexception", this, o, response, e);
14743             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14744             return;
14745         }
14746         
14747         this.fireEvent("load", this, o, o.request.arg);
14748         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14749     },
14750
14751     // private
14752     update : function(dataSet){
14753
14754     },
14755
14756     // private
14757     updateResponse : function(dataSet){
14758
14759     }
14760 });/*
14761  * Based on:
14762  * Ext JS Library 1.1.1
14763  * Copyright(c) 2006-2007, Ext JS, LLC.
14764  *
14765  * Originally Released Under LGPL - original licence link has changed is not relivant.
14766  *
14767  * Fork - LGPL
14768  * <script type="text/javascript">
14769  */
14770
14771 /**
14772  * @class Roo.data.ScriptTagProxy
14773  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14774  * other than the originating domain of the running page.<br><br>
14775  * <p>
14776  * <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
14777  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14778  * <p>
14779  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14780  * source code that is used as the source inside a &lt;script> tag.<br><br>
14781  * <p>
14782  * In order for the browser to process the returned data, the server must wrap the data object
14783  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14784  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14785  * depending on whether the callback name was passed:
14786  * <p>
14787  * <pre><code>
14788 boolean scriptTag = false;
14789 String cb = request.getParameter("callback");
14790 if (cb != null) {
14791     scriptTag = true;
14792     response.setContentType("text/javascript");
14793 } else {
14794     response.setContentType("application/x-json");
14795 }
14796 Writer out = response.getWriter();
14797 if (scriptTag) {
14798     out.write(cb + "(");
14799 }
14800 out.print(dataBlock.toJsonString());
14801 if (scriptTag) {
14802     out.write(");");
14803 }
14804 </pre></code>
14805  *
14806  * @constructor
14807  * @param {Object} config A configuration object.
14808  */
14809 Roo.data.ScriptTagProxy = function(config){
14810     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14811     Roo.apply(this, config);
14812     this.head = document.getElementsByTagName("head")[0];
14813 };
14814
14815 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14816
14817 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14818     /**
14819      * @cfg {String} url The URL from which to request the data object.
14820      */
14821     /**
14822      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14823      */
14824     timeout : 30000,
14825     /**
14826      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14827      * the server the name of the callback function set up by the load call to process the returned data object.
14828      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14829      * javascript output which calls this named function passing the data object as its only parameter.
14830      */
14831     callbackParam : "callback",
14832     /**
14833      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14834      * name to the request.
14835      */
14836     nocache : true,
14837
14838     /**
14839      * Load data from the configured URL, read the data object into
14840      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14841      * process that block using the passed callback.
14842      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14843      * for the request to the remote server.
14844      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14845      * object into a block of Roo.data.Records.
14846      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14847      * The function must be passed <ul>
14848      * <li>The Record block object</li>
14849      * <li>The "arg" argument from the load function</li>
14850      * <li>A boolean success indicator</li>
14851      * </ul>
14852      * @param {Object} scope The scope in which to call the callback
14853      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14854      */
14855     load : function(params, reader, callback, scope, arg){
14856         if(this.fireEvent("beforeload", this, params) !== false){
14857
14858             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14859
14860             var url = this.url;
14861             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14862             if(this.nocache){
14863                 url += "&_dc=" + (new Date().getTime());
14864             }
14865             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14866             var trans = {
14867                 id : transId,
14868                 cb : "stcCallback"+transId,
14869                 scriptId : "stcScript"+transId,
14870                 params : params,
14871                 arg : arg,
14872                 url : url,
14873                 callback : callback,
14874                 scope : scope,
14875                 reader : reader
14876             };
14877             var conn = this;
14878
14879             window[trans.cb] = function(o){
14880                 conn.handleResponse(o, trans);
14881             };
14882
14883             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14884
14885             if(this.autoAbort !== false){
14886                 this.abort();
14887             }
14888
14889             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14890
14891             var script = document.createElement("script");
14892             script.setAttribute("src", url);
14893             script.setAttribute("type", "text/javascript");
14894             script.setAttribute("id", trans.scriptId);
14895             this.head.appendChild(script);
14896
14897             this.trans = trans;
14898         }else{
14899             callback.call(scope||this, null, arg, false);
14900         }
14901     },
14902
14903     // private
14904     isLoading : function(){
14905         return this.trans ? true : false;
14906     },
14907
14908     /**
14909      * Abort the current server request.
14910      */
14911     abort : function(){
14912         if(this.isLoading()){
14913             this.destroyTrans(this.trans);
14914         }
14915     },
14916
14917     // private
14918     destroyTrans : function(trans, isLoaded){
14919         this.head.removeChild(document.getElementById(trans.scriptId));
14920         clearTimeout(trans.timeoutId);
14921         if(isLoaded){
14922             window[trans.cb] = undefined;
14923             try{
14924                 delete window[trans.cb];
14925             }catch(e){}
14926         }else{
14927             // if hasn't been loaded, wait for load to remove it to prevent script error
14928             window[trans.cb] = function(){
14929                 window[trans.cb] = undefined;
14930                 try{
14931                     delete window[trans.cb];
14932                 }catch(e){}
14933             };
14934         }
14935     },
14936
14937     // private
14938     handleResponse : function(o, trans){
14939         this.trans = false;
14940         this.destroyTrans(trans, true);
14941         var result;
14942         try {
14943             result = trans.reader.readRecords(o);
14944         }catch(e){
14945             this.fireEvent("loadexception", this, o, trans.arg, e);
14946             trans.callback.call(trans.scope||window, null, trans.arg, false);
14947             return;
14948         }
14949         this.fireEvent("load", this, o, trans.arg);
14950         trans.callback.call(trans.scope||window, result, trans.arg, true);
14951     },
14952
14953     // private
14954     handleFailure : function(trans){
14955         this.trans = false;
14956         this.destroyTrans(trans, false);
14957         this.fireEvent("loadexception", this, null, trans.arg);
14958         trans.callback.call(trans.scope||window, null, trans.arg, false);
14959     }
14960 });/*
14961  * Based on:
14962  * Ext JS Library 1.1.1
14963  * Copyright(c) 2006-2007, Ext JS, LLC.
14964  *
14965  * Originally Released Under LGPL - original licence link has changed is not relivant.
14966  *
14967  * Fork - LGPL
14968  * <script type="text/javascript">
14969  */
14970
14971 /**
14972  * @class Roo.data.JsonReader
14973  * @extends Roo.data.DataReader
14974  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14975  * based on mappings in a provided Roo.data.Record constructor.
14976  * 
14977  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14978  * in the reply previously. 
14979  * 
14980  * <p>
14981  * Example code:
14982  * <pre><code>
14983 var RecordDef = Roo.data.Record.create([
14984     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14985     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14986 ]);
14987 var myReader = new Roo.data.JsonReader({
14988     totalProperty: "results",    // The property which contains the total dataset size (optional)
14989     root: "rows",                // The property which contains an Array of row objects
14990     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14991 }, RecordDef);
14992 </code></pre>
14993  * <p>
14994  * This would consume a JSON file like this:
14995  * <pre><code>
14996 { 'results': 2, 'rows': [
14997     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14998     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14999 }
15000 </code></pre>
15001  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15002  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15003  * paged from the remote server.
15004  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15005  * @cfg {String} root name of the property which contains the Array of row objects.
15006  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15007  * @cfg {Array} fields Array of field definition objects
15008  * @constructor
15009  * Create a new JsonReader
15010  * @param {Object} meta Metadata configuration options
15011  * @param {Object} recordType Either an Array of field definition objects,
15012  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15013  */
15014 Roo.data.JsonReader = function(meta, recordType){
15015     
15016     meta = meta || {};
15017     // set some defaults:
15018     Roo.applyIf(meta, {
15019         totalProperty: 'total',
15020         successProperty : 'success',
15021         root : 'data',
15022         id : 'id'
15023     });
15024     
15025     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15026 };
15027 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15028     
15029     readerType : 'Json',
15030     
15031     /**
15032      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15033      * Used by Store query builder to append _requestMeta to params.
15034      * 
15035      */
15036     metaFromRemote : false,
15037     /**
15038      * This method is only used by a DataProxy which has retrieved data from a remote server.
15039      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15040      * @return {Object} data A data block which is used by an Roo.data.Store object as
15041      * a cache of Roo.data.Records.
15042      */
15043     read : function(response){
15044         var json = response.responseText;
15045        
15046         var o = /* eval:var:o */ eval("("+json+")");
15047         if(!o) {
15048             throw {message: "JsonReader.read: Json object not found"};
15049         }
15050         
15051         if(o.metaData){
15052             
15053             delete this.ef;
15054             this.metaFromRemote = true;
15055             this.meta = o.metaData;
15056             this.recordType = Roo.data.Record.create(o.metaData.fields);
15057             this.onMetaChange(this.meta, this.recordType, o);
15058         }
15059         return this.readRecords(o);
15060     },
15061
15062     // private function a store will implement
15063     onMetaChange : function(meta, recordType, o){
15064
15065     },
15066
15067     /**
15068          * @ignore
15069          */
15070     simpleAccess: function(obj, subsc) {
15071         return obj[subsc];
15072     },
15073
15074         /**
15075          * @ignore
15076          */
15077     getJsonAccessor: function(){
15078         var re = /[\[\.]/;
15079         return function(expr) {
15080             try {
15081                 return(re.test(expr))
15082                     ? new Function("obj", "return obj." + expr)
15083                     : function(obj){
15084                         return obj[expr];
15085                     };
15086             } catch(e){}
15087             return Roo.emptyFn;
15088         };
15089     }(),
15090
15091     /**
15092      * Create a data block containing Roo.data.Records from an XML document.
15093      * @param {Object} o An object which contains an Array of row objects in the property specified
15094      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15095      * which contains the total size of the dataset.
15096      * @return {Object} data A data block which is used by an Roo.data.Store object as
15097      * a cache of Roo.data.Records.
15098      */
15099     readRecords : function(o){
15100         /**
15101          * After any data loads, the raw JSON data is available for further custom processing.
15102          * @type Object
15103          */
15104         this.o = o;
15105         var s = this.meta, Record = this.recordType,
15106             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15107
15108 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15109         if (!this.ef) {
15110             if(s.totalProperty) {
15111                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15112                 }
15113                 if(s.successProperty) {
15114                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15115                 }
15116                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15117                 if (s.id) {
15118                         var g = this.getJsonAccessor(s.id);
15119                         this.getId = function(rec) {
15120                                 var r = g(rec);  
15121                                 return (r === undefined || r === "") ? null : r;
15122                         };
15123                 } else {
15124                         this.getId = function(){return null;};
15125                 }
15126             this.ef = [];
15127             for(var jj = 0; jj < fl; jj++){
15128                 f = fi[jj];
15129                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15130                 this.ef[jj] = this.getJsonAccessor(map);
15131             }
15132         }
15133
15134         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15135         if(s.totalProperty){
15136             var vt = parseInt(this.getTotal(o), 10);
15137             if(!isNaN(vt)){
15138                 totalRecords = vt;
15139             }
15140         }
15141         if(s.successProperty){
15142             var vs = this.getSuccess(o);
15143             if(vs === false || vs === 'false'){
15144                 success = false;
15145             }
15146         }
15147         var records = [];
15148         for(var i = 0; i < c; i++){
15149                 var n = root[i];
15150             var values = {};
15151             var id = this.getId(n);
15152             for(var j = 0; j < fl; j++){
15153                 f = fi[j];
15154             var v = this.ef[j](n);
15155             if (!f.convert) {
15156                 Roo.log('missing convert for ' + f.name);
15157                 Roo.log(f);
15158                 continue;
15159             }
15160             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15161             }
15162             var record = new Record(values, id);
15163             record.json = n;
15164             records[i] = record;
15165         }
15166         return {
15167             raw : o,
15168             success : success,
15169             records : records,
15170             totalRecords : totalRecords
15171         };
15172     },
15173     // used when loading children.. @see loadDataFromChildren
15174     toLoadData: function(rec)
15175     {
15176         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15177         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15178         return { data : data, total : data.length };
15179         
15180     }
15181 });/*
15182  * Based on:
15183  * Ext JS Library 1.1.1
15184  * Copyright(c) 2006-2007, Ext JS, LLC.
15185  *
15186  * Originally Released Under LGPL - original licence link has changed is not relivant.
15187  *
15188  * Fork - LGPL
15189  * <script type="text/javascript">
15190  */
15191
15192 /**
15193  * @class Roo.data.ArrayReader
15194  * @extends Roo.data.DataReader
15195  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15196  * Each element of that Array represents a row of data fields. The
15197  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15198  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15199  * <p>
15200  * Example code:.
15201  * <pre><code>
15202 var RecordDef = Roo.data.Record.create([
15203     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15204     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15205 ]);
15206 var myReader = new Roo.data.ArrayReader({
15207     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15208 }, RecordDef);
15209 </code></pre>
15210  * <p>
15211  * This would consume an Array like this:
15212  * <pre><code>
15213 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15214   </code></pre>
15215  
15216  * @constructor
15217  * Create a new JsonReader
15218  * @param {Object} meta Metadata configuration options.
15219  * @param {Object|Array} recordType Either an Array of field definition objects
15220  * 
15221  * @cfg {Array} fields Array of field definition objects
15222  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15223  * as specified to {@link Roo.data.Record#create},
15224  * or an {@link Roo.data.Record} object
15225  *
15226  * 
15227  * created using {@link Roo.data.Record#create}.
15228  */
15229 Roo.data.ArrayReader = function(meta, recordType)
15230 {    
15231     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15232 };
15233
15234 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15235     
15236       /**
15237      * Create a data block containing Roo.data.Records from an XML document.
15238      * @param {Object} o An Array of row objects which represents the dataset.
15239      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15240      * a cache of Roo.data.Records.
15241      */
15242     readRecords : function(o)
15243     {
15244         var sid = this.meta ? this.meta.id : null;
15245         var recordType = this.recordType, fields = recordType.prototype.fields;
15246         var records = [];
15247         var root = o;
15248         for(var i = 0; i < root.length; i++){
15249             var n = root[i];
15250             var values = {};
15251             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15252             for(var j = 0, jlen = fields.length; j < jlen; j++){
15253                 var f = fields.items[j];
15254                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15255                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15256                 v = f.convert(v);
15257                 values[f.name] = v;
15258             }
15259             var record = new recordType(values, id);
15260             record.json = n;
15261             records[records.length] = record;
15262         }
15263         return {
15264             records : records,
15265             totalRecords : records.length
15266         };
15267     },
15268     // used when loading children.. @see loadDataFromChildren
15269     toLoadData: function(rec)
15270     {
15271         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15272         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15273         
15274     }
15275     
15276     
15277 });/*
15278  * - LGPL
15279  * * 
15280  */
15281
15282 /**
15283  * @class Roo.bootstrap.ComboBox
15284  * @extends Roo.bootstrap.TriggerField
15285  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15286  * @cfg {Boolean} append (true|false) default false
15287  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15288  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15289  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15290  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15291  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15292  * @cfg {Boolean} animate default true
15293  * @cfg {Boolean} emptyResultText only for touch device
15294  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15295  * @cfg {String} emptyTitle default ''
15296  * @cfg {Number} width fixed with? experimental
15297  * @constructor
15298  * Create a new ComboBox.
15299  * @param {Object} config Configuration options
15300  */
15301 Roo.bootstrap.ComboBox = function(config){
15302     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15303     this.addEvents({
15304         /**
15305          * @event expand
15306          * Fires when the dropdown list is expanded
15307         * @param {Roo.bootstrap.ComboBox} combo This combo box
15308         */
15309         'expand' : true,
15310         /**
15311          * @event collapse
15312          * Fires when the dropdown list is collapsed
15313         * @param {Roo.bootstrap.ComboBox} combo This combo box
15314         */
15315         'collapse' : true,
15316         /**
15317          * @event beforeselect
15318          * Fires before a list item is selected. Return false to cancel the selection.
15319         * @param {Roo.bootstrap.ComboBox} combo This combo box
15320         * @param {Roo.data.Record} record The data record returned from the underlying store
15321         * @param {Number} index The index of the selected item in the dropdown list
15322         */
15323         'beforeselect' : true,
15324         /**
15325          * @event select
15326          * Fires when a list item is selected
15327         * @param {Roo.bootstrap.ComboBox} combo This combo box
15328         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15329         * @param {Number} index The index of the selected item in the dropdown list
15330         */
15331         'select' : true,
15332         /**
15333          * @event beforequery
15334          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15335          * The event object passed has these properties:
15336         * @param {Roo.bootstrap.ComboBox} combo This combo box
15337         * @param {String} query The query
15338         * @param {Boolean} forceAll true to force "all" query
15339         * @param {Boolean} cancel true to cancel the query
15340         * @param {Object} e The query event object
15341         */
15342         'beforequery': true,
15343          /**
15344          * @event add
15345          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15346         * @param {Roo.bootstrap.ComboBox} combo This combo box
15347         */
15348         'add' : true,
15349         /**
15350          * @event edit
15351          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15352         * @param {Roo.bootstrap.ComboBox} combo This combo box
15353         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15354         */
15355         'edit' : true,
15356         /**
15357          * @event remove
15358          * Fires when the remove value from the combobox array
15359         * @param {Roo.bootstrap.ComboBox} combo This combo box
15360         */
15361         'remove' : true,
15362         /**
15363          * @event afterremove
15364          * Fires when the remove value from the combobox array
15365         * @param {Roo.bootstrap.ComboBox} combo This combo box
15366         */
15367         'afterremove' : true,
15368         /**
15369          * @event specialfilter
15370          * Fires when specialfilter
15371             * @param {Roo.bootstrap.ComboBox} combo This combo box
15372             */
15373         'specialfilter' : true,
15374         /**
15375          * @event tick
15376          * Fires when tick the element
15377             * @param {Roo.bootstrap.ComboBox} combo This combo box
15378             */
15379         'tick' : true,
15380         /**
15381          * @event touchviewdisplay
15382          * Fires when touch view require special display (default is using displayField)
15383             * @param {Roo.bootstrap.ComboBox} combo This combo box
15384             * @param {Object} cfg set html .
15385             */
15386         'touchviewdisplay' : true
15387         
15388     });
15389     
15390     this.item = [];
15391     this.tickItems = [];
15392     
15393     this.selectedIndex = -1;
15394     if(this.mode == 'local'){
15395         if(config.queryDelay === undefined){
15396             this.queryDelay = 10;
15397         }
15398         if(config.minChars === undefined){
15399             this.minChars = 0;
15400         }
15401     }
15402 };
15403
15404 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15405      
15406     /**
15407      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15408      * rendering into an Roo.Editor, defaults to false)
15409      */
15410     /**
15411      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15412      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15413      */
15414     /**
15415      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15416      */
15417     /**
15418      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15419      * the dropdown list (defaults to undefined, with no header element)
15420      */
15421
15422      /**
15423      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15424      */
15425      
15426      /**
15427      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15428      */
15429     listWidth: undefined,
15430     /**
15431      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15432      * mode = 'remote' or 'text' if mode = 'local')
15433      */
15434     displayField: undefined,
15435     
15436     /**
15437      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15438      * mode = 'remote' or 'value' if mode = 'local'). 
15439      * Note: use of a valueField requires the user make a selection
15440      * in order for a value to be mapped.
15441      */
15442     valueField: undefined,
15443     /**
15444      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15445      */
15446     modalTitle : '',
15447     
15448     /**
15449      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15450      * field's data value (defaults to the underlying DOM element's name)
15451      */
15452     hiddenName: undefined,
15453     /**
15454      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15455      */
15456     listClass: '',
15457     /**
15458      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15459      */
15460     selectedClass: 'active',
15461     
15462     /**
15463      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15464      */
15465     shadow:'sides',
15466     /**
15467      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15468      * anchor positions (defaults to 'tl-bl')
15469      */
15470     listAlign: 'tl-bl?',
15471     /**
15472      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15473      */
15474     maxHeight: 300,
15475     /**
15476      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15477      * query specified by the allQuery config option (defaults to 'query')
15478      */
15479     triggerAction: 'query',
15480     /**
15481      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15482      * (defaults to 4, does not apply if editable = false)
15483      */
15484     minChars : 4,
15485     /**
15486      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15487      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15488      */
15489     typeAhead: false,
15490     /**
15491      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15492      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15493      */
15494     queryDelay: 500,
15495     /**
15496      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15497      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15498      */
15499     pageSize: 0,
15500     /**
15501      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15502      * when editable = true (defaults to false)
15503      */
15504     selectOnFocus:false,
15505     /**
15506      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15507      */
15508     queryParam: 'query',
15509     /**
15510      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15511      * when mode = 'remote' (defaults to 'Loading...')
15512      */
15513     loadingText: 'Loading...',
15514     /**
15515      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15516      */
15517     resizable: false,
15518     /**
15519      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15520      */
15521     handleHeight : 8,
15522     /**
15523      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15524      * traditional select (defaults to true)
15525      */
15526     editable: true,
15527     /**
15528      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15529      */
15530     allQuery: '',
15531     /**
15532      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15533      */
15534     mode: 'remote',
15535     /**
15536      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15537      * listWidth has a higher value)
15538      */
15539     minListWidth : 70,
15540     /**
15541      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15542      * allow the user to set arbitrary text into the field (defaults to false)
15543      */
15544     forceSelection:false,
15545     /**
15546      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15547      * if typeAhead = true (defaults to 250)
15548      */
15549     typeAheadDelay : 250,
15550     /**
15551      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15552      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15553      */
15554     valueNotFoundText : undefined,
15555     /**
15556      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15557      */
15558     blockFocus : false,
15559     
15560     /**
15561      * @cfg {Boolean} disableClear Disable showing of clear button.
15562      */
15563     disableClear : false,
15564     /**
15565      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15566      */
15567     alwaysQuery : false,
15568     
15569     /**
15570      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15571      */
15572     multiple : false,
15573     
15574     /**
15575      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15576      */
15577     invalidClass : "has-warning",
15578     
15579     /**
15580      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15581      */
15582     validClass : "has-success",
15583     
15584     /**
15585      * @cfg {Boolean} specialFilter (true|false) special filter default false
15586      */
15587     specialFilter : false,
15588     
15589     /**
15590      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15591      */
15592     mobileTouchView : true,
15593     
15594     /**
15595      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15596      */
15597     useNativeIOS : false,
15598     
15599     /**
15600      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15601      */
15602     mobile_restrict_height : false,
15603     
15604     ios_options : false,
15605     
15606     //private
15607     addicon : false,
15608     editicon: false,
15609     
15610     page: 0,
15611     hasQuery: false,
15612     append: false,
15613     loadNext: false,
15614     autoFocus : true,
15615     tickable : false,
15616     btnPosition : 'right',
15617     triggerList : true,
15618     showToggleBtn : true,
15619     animate : true,
15620     emptyResultText: 'Empty',
15621     triggerText : 'Select',
15622     emptyTitle : '',
15623     width : false,
15624     
15625     // element that contains real text value.. (when hidden is used..)
15626     
15627     getAutoCreate : function()
15628     {   
15629         var cfg = false;
15630         //render
15631         /*
15632          * Render classic select for iso
15633          */
15634         
15635         if(Roo.isIOS && this.useNativeIOS){
15636             cfg = this.getAutoCreateNativeIOS();
15637             return cfg;
15638         }
15639         
15640         /*
15641          * Touch Devices
15642          */
15643         
15644         if(Roo.isTouch && this.mobileTouchView){
15645             cfg = this.getAutoCreateTouchView();
15646             return cfg;;
15647         }
15648         
15649         /*
15650          *  Normal ComboBox
15651          */
15652         if(!this.tickable){
15653             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15654             return cfg;
15655         }
15656         
15657         /*
15658          *  ComboBox with tickable selections
15659          */
15660              
15661         var align = this.labelAlign || this.parentLabelAlign();
15662         
15663         cfg = {
15664             cls : 'form-group roo-combobox-tickable' //input-group
15665         };
15666         
15667         var btn_text_select = '';
15668         var btn_text_done = '';
15669         var btn_text_cancel = '';
15670         
15671         if (this.btn_text_show) {
15672             btn_text_select = 'Select';
15673             btn_text_done = 'Done';
15674             btn_text_cancel = 'Cancel'; 
15675         }
15676         
15677         var buttons = {
15678             tag : 'div',
15679             cls : 'tickable-buttons',
15680             cn : [
15681                 {
15682                     tag : 'button',
15683                     type : 'button',
15684                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15685                     //html : this.triggerText
15686                     html: btn_text_select
15687                 },
15688                 {
15689                     tag : 'button',
15690                     type : 'button',
15691                     name : 'ok',
15692                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15693                     //html : 'Done'
15694                     html: btn_text_done
15695                 },
15696                 {
15697                     tag : 'button',
15698                     type : 'button',
15699                     name : 'cancel',
15700                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15701                     //html : 'Cancel'
15702                     html: btn_text_cancel
15703                 }
15704             ]
15705         };
15706         
15707         if(this.editable){
15708             buttons.cn.unshift({
15709                 tag: 'input',
15710                 cls: 'roo-select2-search-field-input'
15711             });
15712         }
15713         
15714         var _this = this;
15715         
15716         Roo.each(buttons.cn, function(c){
15717             if (_this.size) {
15718                 c.cls += ' btn-' + _this.size;
15719             }
15720
15721             if (_this.disabled) {
15722                 c.disabled = true;
15723             }
15724         });
15725         
15726         var box = {
15727             tag: 'div',
15728             style : 'display: contents',
15729             cn: [
15730                 {
15731                     tag: 'input',
15732                     type : 'hidden',
15733                     cls: 'form-hidden-field'
15734                 },
15735                 {
15736                     tag: 'ul',
15737                     cls: 'roo-select2-choices',
15738                     cn:[
15739                         {
15740                             tag: 'li',
15741                             cls: 'roo-select2-search-field',
15742                             cn: [
15743                                 buttons
15744                             ]
15745                         }
15746                     ]
15747                 }
15748             ]
15749         };
15750         
15751         var combobox = {
15752             cls: 'roo-select2-container input-group roo-select2-container-multi',
15753             cn: [
15754                 
15755                 box
15756 //                {
15757 //                    tag: 'ul',
15758 //                    cls: 'typeahead typeahead-long dropdown-menu',
15759 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15760 //                }
15761             ]
15762         };
15763         
15764         if(this.hasFeedback && !this.allowBlank){
15765             
15766             var feedback = {
15767                 tag: 'span',
15768                 cls: 'glyphicon form-control-feedback'
15769             };
15770
15771             combobox.cn.push(feedback);
15772         }
15773         
15774         
15775         
15776         var indicator = {
15777             tag : 'i',
15778             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15779             tooltip : 'This field is required'
15780         };
15781         if (Roo.bootstrap.version == 4) {
15782             indicator = {
15783                 tag : 'i',
15784                 style : 'display:none'
15785             };
15786         }
15787         if (align ==='left' && this.fieldLabel.length) {
15788             
15789             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15790             
15791             cfg.cn = [
15792                 indicator,
15793                 {
15794                     tag: 'label',
15795                     'for' :  id,
15796                     cls : 'control-label col-form-label',
15797                     html : this.fieldLabel
15798
15799                 },
15800                 {
15801                     cls : "", 
15802                     cn: [
15803                         combobox
15804                     ]
15805                 }
15806
15807             ];
15808             
15809             var labelCfg = cfg.cn[1];
15810             var contentCfg = cfg.cn[2];
15811             
15812
15813             if(this.indicatorpos == 'right'){
15814                 
15815                 cfg.cn = [
15816                     {
15817                         tag: 'label',
15818                         'for' :  id,
15819                         cls : 'control-label col-form-label',
15820                         cn : [
15821                             {
15822                                 tag : 'span',
15823                                 html : this.fieldLabel
15824                             },
15825                             indicator
15826                         ]
15827                     },
15828                     {
15829                         cls : "",
15830                         cn: [
15831                             combobox
15832                         ]
15833                     }
15834
15835                 ];
15836                 
15837                 
15838                 
15839                 labelCfg = cfg.cn[0];
15840                 contentCfg = cfg.cn[1];
15841             
15842             }
15843             
15844             if(this.labelWidth > 12){
15845                 labelCfg.style = "width: " + this.labelWidth + 'px';
15846             }
15847             if(this.width * 1 > 0){
15848                 contentCfg.style = "width: " + this.width + 'px';
15849             }
15850             if(this.labelWidth < 13 && this.labelmd == 0){
15851                 this.labelmd = this.labelWidth;
15852             }
15853             
15854             if(this.labellg > 0){
15855                 labelCfg.cls += ' col-lg-' + this.labellg;
15856                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15857             }
15858             
15859             if(this.labelmd > 0){
15860                 labelCfg.cls += ' col-md-' + this.labelmd;
15861                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15862             }
15863             
15864             if(this.labelsm > 0){
15865                 labelCfg.cls += ' col-sm-' + this.labelsm;
15866                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15867             }
15868             
15869             if(this.labelxs > 0){
15870                 labelCfg.cls += ' col-xs-' + this.labelxs;
15871                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15872             }
15873                 
15874                 
15875         } else if ( this.fieldLabel.length) {
15876 //                Roo.log(" label");
15877                  cfg.cn = [
15878                    indicator,
15879                     {
15880                         tag: 'label',
15881                         //cls : 'input-group-addon',
15882                         html : this.fieldLabel
15883                     },
15884                     combobox
15885                 ];
15886                 
15887                 if(this.indicatorpos == 'right'){
15888                     cfg.cn = [
15889                         {
15890                             tag: 'label',
15891                             //cls : 'input-group-addon',
15892                             html : this.fieldLabel
15893                         },
15894                         indicator,
15895                         combobox
15896                     ];
15897                     
15898                 }
15899
15900         } else {
15901             
15902 //                Roo.log(" no label && no align");
15903                 cfg = combobox
15904                      
15905                 
15906         }
15907          
15908         var settings=this;
15909         ['xs','sm','md','lg'].map(function(size){
15910             if (settings[size]) {
15911                 cfg.cls += ' col-' + size + '-' + settings[size];
15912             }
15913         });
15914         
15915         return cfg;
15916         
15917     },
15918     
15919     _initEventsCalled : false,
15920     
15921     // private
15922     initEvents: function()
15923     {   
15924         if (this._initEventsCalled) { // as we call render... prevent looping...
15925             return;
15926         }
15927         this._initEventsCalled = true;
15928         
15929         if (!this.store) {
15930             throw "can not find store for combo";
15931         }
15932         
15933         this.indicator = this.indicatorEl();
15934         
15935         this.store = Roo.factory(this.store, Roo.data);
15936         this.store.parent = this;
15937         
15938         // if we are building from html. then this element is so complex, that we can not really
15939         // use the rendered HTML.
15940         // so we have to trash and replace the previous code.
15941         if (Roo.XComponent.build_from_html) {
15942             // remove this element....
15943             var e = this.el.dom, k=0;
15944             while (e ) { e = e.previousSibling;  ++k;}
15945
15946             this.el.remove();
15947             
15948             this.el=false;
15949             this.rendered = false;
15950             
15951             this.render(this.parent().getChildContainer(true), k);
15952         }
15953         
15954         if(Roo.isIOS && this.useNativeIOS){
15955             this.initIOSView();
15956             return;
15957         }
15958         
15959         /*
15960          * Touch Devices
15961          */
15962         
15963         if(Roo.isTouch && this.mobileTouchView){
15964             this.initTouchView();
15965             return;
15966         }
15967         
15968         if(this.tickable){
15969             this.initTickableEvents();
15970             return;
15971         }
15972         
15973         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15974         
15975         if(this.hiddenName){
15976             
15977             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15978             
15979             this.hiddenField.dom.value =
15980                 this.hiddenValue !== undefined ? this.hiddenValue :
15981                 this.value !== undefined ? this.value : '';
15982
15983             // prevent input submission
15984             this.el.dom.removeAttribute('name');
15985             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15986              
15987              
15988         }
15989         //if(Roo.isGecko){
15990         //    this.el.dom.setAttribute('autocomplete', 'off');
15991         //}
15992         
15993         var cls = 'x-combo-list';
15994         
15995         //this.list = new Roo.Layer({
15996         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15997         //});
15998         
15999         var _this = this;
16000         
16001         (function(){
16002             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16003             _this.list.setWidth(lw);
16004         }).defer(100);
16005         
16006         this.list.on('mouseover', this.onViewOver, this);
16007         this.list.on('mousemove', this.onViewMove, this);
16008         this.list.on('scroll', this.onViewScroll, this);
16009         
16010         /*
16011         this.list.swallowEvent('mousewheel');
16012         this.assetHeight = 0;
16013
16014         if(this.title){
16015             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16016             this.assetHeight += this.header.getHeight();
16017         }
16018
16019         this.innerList = this.list.createChild({cls:cls+'-inner'});
16020         this.innerList.on('mouseover', this.onViewOver, this);
16021         this.innerList.on('mousemove', this.onViewMove, this);
16022         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16023         
16024         if(this.allowBlank && !this.pageSize && !this.disableClear){
16025             this.footer = this.list.createChild({cls:cls+'-ft'});
16026             this.pageTb = new Roo.Toolbar(this.footer);
16027            
16028         }
16029         if(this.pageSize){
16030             this.footer = this.list.createChild({cls:cls+'-ft'});
16031             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16032                     {pageSize: this.pageSize});
16033             
16034         }
16035         
16036         if (this.pageTb && this.allowBlank && !this.disableClear) {
16037             var _this = this;
16038             this.pageTb.add(new Roo.Toolbar.Fill(), {
16039                 cls: 'x-btn-icon x-btn-clear',
16040                 text: '&#160;',
16041                 handler: function()
16042                 {
16043                     _this.collapse();
16044                     _this.clearValue();
16045                     _this.onSelect(false, -1);
16046                 }
16047             });
16048         }
16049         if (this.footer) {
16050             this.assetHeight += this.footer.getHeight();
16051         }
16052         */
16053             
16054         if(!this.tpl){
16055             this.tpl = Roo.bootstrap.version == 4 ?
16056                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16057                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16058         }
16059
16060         this.view = new Roo.View(this.list, this.tpl, {
16061             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16062         });
16063         //this.view.wrapEl.setDisplayed(false);
16064         this.view.on('click', this.onViewClick, this);
16065         
16066         
16067         this.store.on('beforeload', this.onBeforeLoad, this);
16068         this.store.on('load', this.onLoad, this);
16069         this.store.on('loadexception', this.onLoadException, this);
16070         /*
16071         if(this.resizable){
16072             this.resizer = new Roo.Resizable(this.list,  {
16073                pinned:true, handles:'se'
16074             });
16075             this.resizer.on('resize', function(r, w, h){
16076                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16077                 this.listWidth = w;
16078                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16079                 this.restrictHeight();
16080             }, this);
16081             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16082         }
16083         */
16084         if(!this.editable){
16085             this.editable = true;
16086             this.setEditable(false);
16087         }
16088         
16089         /*
16090         
16091         if (typeof(this.events.add.listeners) != 'undefined') {
16092             
16093             this.addicon = this.wrap.createChild(
16094                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16095        
16096             this.addicon.on('click', function(e) {
16097                 this.fireEvent('add', this);
16098             }, this);
16099         }
16100         if (typeof(this.events.edit.listeners) != 'undefined') {
16101             
16102             this.editicon = this.wrap.createChild(
16103                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16104             if (this.addicon) {
16105                 this.editicon.setStyle('margin-left', '40px');
16106             }
16107             this.editicon.on('click', function(e) {
16108                 
16109                 // we fire even  if inothing is selected..
16110                 this.fireEvent('edit', this, this.lastData );
16111                 
16112             }, this);
16113         }
16114         */
16115         
16116         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16117             "up" : function(e){
16118                 this.inKeyMode = true;
16119                 this.selectPrev();
16120             },
16121
16122             "down" : function(e){
16123                 if(!this.isExpanded()){
16124                     this.onTriggerClick();
16125                 }else{
16126                     this.inKeyMode = true;
16127                     this.selectNext();
16128                 }
16129             },
16130
16131             "enter" : function(e){
16132 //                this.onViewClick();
16133                 //return true;
16134                 this.collapse();
16135                 
16136                 if(this.fireEvent("specialkey", this, e)){
16137                     this.onViewClick(false);
16138                 }
16139                 
16140                 return true;
16141             },
16142
16143             "esc" : function(e){
16144                 this.collapse();
16145             },
16146
16147             "tab" : function(e){
16148                 this.collapse();
16149                 
16150                 if(this.fireEvent("specialkey", this, e)){
16151                     this.onViewClick(false);
16152                 }
16153                 
16154                 return true;
16155             },
16156
16157             scope : this,
16158
16159             doRelay : function(foo, bar, hname){
16160                 if(hname == 'down' || this.scope.isExpanded()){
16161                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16162                 }
16163                 return true;
16164             },
16165
16166             forceKeyDown: true
16167         });
16168         
16169         
16170         this.queryDelay = Math.max(this.queryDelay || 10,
16171                 this.mode == 'local' ? 10 : 250);
16172         
16173         
16174         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16175         
16176         if(this.typeAhead){
16177             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16178         }
16179         if(this.editable !== false){
16180             this.inputEl().on("keyup", this.onKeyUp, this);
16181         }
16182         if(this.forceSelection){
16183             this.inputEl().on('blur', this.doForce, this);
16184         }
16185         
16186         if(this.multiple){
16187             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16188             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16189         }
16190     },
16191     
16192     initTickableEvents: function()
16193     {   
16194         this.createList();
16195         
16196         if(this.hiddenName){
16197             
16198             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16199             
16200             this.hiddenField.dom.value =
16201                 this.hiddenValue !== undefined ? this.hiddenValue :
16202                 this.value !== undefined ? this.value : '';
16203
16204             // prevent input submission
16205             this.el.dom.removeAttribute('name');
16206             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16207              
16208              
16209         }
16210         
16211 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16212         
16213         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16214         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16215         if(this.triggerList){
16216             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16217         }
16218          
16219         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16220         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16221         
16222         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16223         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16224         
16225         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16226         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16227         
16228         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16229         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16230         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16231         
16232         this.okBtn.hide();
16233         this.cancelBtn.hide();
16234         
16235         var _this = this;
16236         
16237         (function(){
16238             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16239             _this.list.setWidth(lw);
16240         }).defer(100);
16241         
16242         this.list.on('mouseover', this.onViewOver, this);
16243         this.list.on('mousemove', this.onViewMove, this);
16244         
16245         this.list.on('scroll', this.onViewScroll, this);
16246         
16247         if(!this.tpl){
16248             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16249                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16250         }
16251
16252         this.view = new Roo.View(this.list, this.tpl, {
16253             singleSelect:true,
16254             tickable:true,
16255             parent:this,
16256             store: this.store,
16257             selectedClass: this.selectedClass
16258         });
16259         
16260         //this.view.wrapEl.setDisplayed(false);
16261         this.view.on('click', this.onViewClick, this);
16262         
16263         
16264         
16265         this.store.on('beforeload', this.onBeforeLoad, this);
16266         this.store.on('load', this.onLoad, this);
16267         this.store.on('loadexception', this.onLoadException, this);
16268         
16269         if(this.editable){
16270             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16271                 "up" : function(e){
16272                     this.inKeyMode = true;
16273                     this.selectPrev();
16274                 },
16275
16276                 "down" : function(e){
16277                     this.inKeyMode = true;
16278                     this.selectNext();
16279                 },
16280
16281                 "enter" : function(e){
16282                     if(this.fireEvent("specialkey", this, e)){
16283                         this.onViewClick(false);
16284                     }
16285                     
16286                     return true;
16287                 },
16288
16289                 "esc" : function(e){
16290                     this.onTickableFooterButtonClick(e, false, false);
16291                 },
16292
16293                 "tab" : function(e){
16294                     this.fireEvent("specialkey", this, e);
16295                     
16296                     this.onTickableFooterButtonClick(e, false, false);
16297                     
16298                     return true;
16299                 },
16300
16301                 scope : this,
16302
16303                 doRelay : function(e, fn, key){
16304                     if(this.scope.isExpanded()){
16305                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16306                     }
16307                     return true;
16308                 },
16309
16310                 forceKeyDown: true
16311             });
16312         }
16313         
16314         this.queryDelay = Math.max(this.queryDelay || 10,
16315                 this.mode == 'local' ? 10 : 250);
16316         
16317         
16318         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16319         
16320         if(this.typeAhead){
16321             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16322         }
16323         
16324         if(this.editable !== false){
16325             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16326         }
16327         
16328         this.indicator = this.indicatorEl();
16329         
16330         if(this.indicator){
16331             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16332             this.indicator.hide();
16333         }
16334         
16335     },
16336
16337     onDestroy : function(){
16338         if(this.view){
16339             this.view.setStore(null);
16340             this.view.el.removeAllListeners();
16341             this.view.el.remove();
16342             this.view.purgeListeners();
16343         }
16344         if(this.list){
16345             this.list.dom.innerHTML  = '';
16346         }
16347         
16348         if(this.store){
16349             this.store.un('beforeload', this.onBeforeLoad, this);
16350             this.store.un('load', this.onLoad, this);
16351             this.store.un('loadexception', this.onLoadException, this);
16352         }
16353         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16354     },
16355
16356     // private
16357     fireKey : function(e){
16358         if(e.isNavKeyPress() && !this.list.isVisible()){
16359             this.fireEvent("specialkey", this, e);
16360         }
16361     },
16362
16363     // private
16364     onResize: function(w, h)
16365     {
16366         
16367         
16368 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16369 //        
16370 //        if(typeof w != 'number'){
16371 //            // we do not handle it!?!?
16372 //            return;
16373 //        }
16374 //        var tw = this.trigger.getWidth();
16375 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16376 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16377 //        var x = w - tw;
16378 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16379 //            
16380 //        //this.trigger.setStyle('left', x+'px');
16381 //        
16382 //        if(this.list && this.listWidth === undefined){
16383 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16384 //            this.list.setWidth(lw);
16385 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16386 //        }
16387         
16388     
16389         
16390     },
16391
16392     /**
16393      * Allow or prevent the user from directly editing the field text.  If false is passed,
16394      * the user will only be able to select from the items defined in the dropdown list.  This method
16395      * is the runtime equivalent of setting the 'editable' config option at config time.
16396      * @param {Boolean} value True to allow the user to directly edit the field text
16397      */
16398     setEditable : function(value){
16399         if(value == this.editable){
16400             return;
16401         }
16402         this.editable = value;
16403         if(!value){
16404             this.inputEl().dom.setAttribute('readOnly', true);
16405             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16406             this.inputEl().addClass('x-combo-noedit');
16407         }else{
16408             this.inputEl().dom.removeAttribute('readOnly');
16409             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16410             this.inputEl().removeClass('x-combo-noedit');
16411         }
16412     },
16413
16414     // private
16415     
16416     onBeforeLoad : function(combo,opts){
16417         if(!this.hasFocus){
16418             return;
16419         }
16420          if (!opts.add) {
16421             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16422          }
16423         this.restrictHeight();
16424         this.selectedIndex = -1;
16425     },
16426
16427     // private
16428     onLoad : function(){
16429         
16430         this.hasQuery = false;
16431         
16432         if(!this.hasFocus){
16433             return;
16434         }
16435         
16436         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16437             this.loading.hide();
16438         }
16439         
16440         if(this.store.getCount() > 0){
16441             
16442             this.expand();
16443             this.restrictHeight();
16444             if(this.lastQuery == this.allQuery){
16445                 if(this.editable && !this.tickable){
16446                     this.inputEl().dom.select();
16447                 }
16448                 
16449                 if(
16450                     !this.selectByValue(this.value, true) &&
16451                     this.autoFocus && 
16452                     (
16453                         !this.store.lastOptions ||
16454                         typeof(this.store.lastOptions.add) == 'undefined' || 
16455                         this.store.lastOptions.add != true
16456                     )
16457                 ){
16458                     this.select(0, true);
16459                 }
16460             }else{
16461                 if(this.autoFocus){
16462                     this.selectNext();
16463                 }
16464                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16465                     this.taTask.delay(this.typeAheadDelay);
16466                 }
16467             }
16468         }else{
16469             this.onEmptyResults();
16470         }
16471         
16472         //this.el.focus();
16473     },
16474     // private
16475     onLoadException : function()
16476     {
16477         this.hasQuery = false;
16478         
16479         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16480             this.loading.hide();
16481         }
16482         
16483         if(this.tickable && this.editable){
16484             return;
16485         }
16486         
16487         this.collapse();
16488         // only causes errors at present
16489         //Roo.log(this.store.reader.jsonData);
16490         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16491             // fixme
16492             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16493         //}
16494         
16495         
16496     },
16497     // private
16498     onTypeAhead : function(){
16499         if(this.store.getCount() > 0){
16500             var r = this.store.getAt(0);
16501             var newValue = r.data[this.displayField];
16502             var len = newValue.length;
16503             var selStart = this.getRawValue().length;
16504             
16505             if(selStart != len){
16506                 this.setRawValue(newValue);
16507                 this.selectText(selStart, newValue.length);
16508             }
16509         }
16510     },
16511
16512     // private
16513     onSelect : function(record, index){
16514         
16515         if(this.fireEvent('beforeselect', this, record, index) !== false){
16516         
16517             this.setFromData(index > -1 ? record.data : false);
16518             
16519             this.collapse();
16520             this.fireEvent('select', this, record, index);
16521         }
16522     },
16523
16524     /**
16525      * Returns the currently selected field value or empty string if no value is set.
16526      * @return {String} value The selected value
16527      */
16528     getValue : function()
16529     {
16530         if(Roo.isIOS && this.useNativeIOS){
16531             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16532         }
16533         
16534         if(this.multiple){
16535             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16536         }
16537         
16538         if(this.valueField){
16539             return typeof this.value != 'undefined' ? this.value : '';
16540         }else{
16541             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16542         }
16543     },
16544     
16545     getRawValue : function()
16546     {
16547         if(Roo.isIOS && this.useNativeIOS){
16548             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16549         }
16550         
16551         var v = this.inputEl().getValue();
16552         
16553         return v;
16554     },
16555
16556     /**
16557      * Clears any text/value currently set in the field
16558      */
16559     clearValue : function(){
16560         
16561         if(this.hiddenField){
16562             this.hiddenField.dom.value = '';
16563         }
16564         this.value = '';
16565         this.setRawValue('');
16566         this.lastSelectionText = '';
16567         this.lastData = false;
16568         
16569         var close = this.closeTriggerEl();
16570         
16571         if(close){
16572             close.hide();
16573         }
16574         
16575         this.validate();
16576         
16577     },
16578
16579     /**
16580      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16581      * will be displayed in the field.  If the value does not match the data value of an existing item,
16582      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16583      * Otherwise the field will be blank (although the value will still be set).
16584      * @param {String} value The value to match
16585      */
16586     setValue : function(v)
16587     {
16588         if(Roo.isIOS && this.useNativeIOS){
16589             this.setIOSValue(v);
16590             return;
16591         }
16592         
16593         if(this.multiple){
16594             this.syncValue();
16595             return;
16596         }
16597         
16598         var text = v;
16599         if(this.valueField){
16600             var r = this.findRecord(this.valueField, v);
16601             if(r){
16602                 text = r.data[this.displayField];
16603             }else if(this.valueNotFoundText !== undefined){
16604                 text = this.valueNotFoundText;
16605             }
16606         }
16607         this.lastSelectionText = text;
16608         if(this.hiddenField){
16609             this.hiddenField.dom.value = v;
16610         }
16611         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16612         this.value = v;
16613         
16614         var close = this.closeTriggerEl();
16615         
16616         if(close){
16617             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16618         }
16619         
16620         this.validate();
16621     },
16622     /**
16623      * @property {Object} the last set data for the element
16624      */
16625     
16626     lastData : false,
16627     /**
16628      * Sets the value of the field based on a object which is related to the record format for the store.
16629      * @param {Object} value the value to set as. or false on reset?
16630      */
16631     setFromData : function(o){
16632         
16633         if(this.multiple){
16634             this.addItem(o);
16635             return;
16636         }
16637             
16638         var dv = ''; // display value
16639         var vv = ''; // value value..
16640         this.lastData = o;
16641         if (this.displayField) {
16642             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16643         } else {
16644             // this is an error condition!!!
16645             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16646         }
16647         
16648         if(this.valueField){
16649             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16650         }
16651         
16652         var close = this.closeTriggerEl();
16653         
16654         if(close){
16655             if(dv.length || vv * 1 > 0){
16656                 close.show() ;
16657                 this.blockFocus=true;
16658             } else {
16659                 close.hide();
16660             }             
16661         }
16662         
16663         if(this.hiddenField){
16664             this.hiddenField.dom.value = vv;
16665             
16666             this.lastSelectionText = dv;
16667             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16668             this.value = vv;
16669             return;
16670         }
16671         // no hidden field.. - we store the value in 'value', but still display
16672         // display field!!!!
16673         this.lastSelectionText = dv;
16674         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16675         this.value = vv;
16676         
16677         
16678         
16679     },
16680     // private
16681     reset : function(){
16682         // overridden so that last data is reset..
16683         
16684         if(this.multiple){
16685             this.clearItem();
16686             return;
16687         }
16688         
16689         this.setValue(this.originalValue);
16690         //this.clearInvalid();
16691         this.lastData = false;
16692         if (this.view) {
16693             this.view.clearSelections();
16694         }
16695         
16696         this.validate();
16697     },
16698     // private
16699     findRecord : function(prop, value){
16700         var record;
16701         if(this.store.getCount() > 0){
16702             this.store.each(function(r){
16703                 if(r.data[prop] == value){
16704                     record = r;
16705                     return false;
16706                 }
16707                 return true;
16708             });
16709         }
16710         return record;
16711     },
16712     
16713     getName: function()
16714     {
16715         // returns hidden if it's set..
16716         if (!this.rendered) {return ''};
16717         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16718         
16719     },
16720     // private
16721     onViewMove : function(e, t){
16722         this.inKeyMode = false;
16723     },
16724
16725     // private
16726     onViewOver : function(e, t){
16727         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16728             return;
16729         }
16730         var item = this.view.findItemFromChild(t);
16731         
16732         if(item){
16733             var index = this.view.indexOf(item);
16734             this.select(index, false);
16735         }
16736     },
16737
16738     // private
16739     onViewClick : function(view, doFocus, el, e)
16740     {
16741         var index = this.view.getSelectedIndexes()[0];
16742         
16743         var r = this.store.getAt(index);
16744         
16745         if(this.tickable){
16746             
16747             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16748                 return;
16749             }
16750             
16751             var rm = false;
16752             var _this = this;
16753             
16754             Roo.each(this.tickItems, function(v,k){
16755                 
16756                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16757                     Roo.log(v);
16758                     _this.tickItems.splice(k, 1);
16759                     
16760                     if(typeof(e) == 'undefined' && view == false){
16761                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16762                     }
16763                     
16764                     rm = true;
16765                     return;
16766                 }
16767             });
16768             
16769             if(rm){
16770                 return;
16771             }
16772             
16773             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16774                 this.tickItems.push(r.data);
16775             }
16776             
16777             if(typeof(e) == 'undefined' && view == false){
16778                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16779             }
16780                     
16781             return;
16782         }
16783         
16784         if(r){
16785             this.onSelect(r, index);
16786         }
16787         if(doFocus !== false && !this.blockFocus){
16788             this.inputEl().focus();
16789         }
16790     },
16791
16792     // private
16793     restrictHeight : function(){
16794         //this.innerList.dom.style.height = '';
16795         //var inner = this.innerList.dom;
16796         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16797         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16798         //this.list.beginUpdate();
16799         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16800         this.list.alignTo(this.inputEl(), this.listAlign);
16801         this.list.alignTo(this.inputEl(), this.listAlign);
16802         //this.list.endUpdate();
16803     },
16804
16805     // private
16806     onEmptyResults : function(){
16807         
16808         if(this.tickable && this.editable){
16809             this.hasFocus = false;
16810             this.restrictHeight();
16811             return;
16812         }
16813         
16814         this.collapse();
16815     },
16816
16817     /**
16818      * Returns true if the dropdown list is expanded, else false.
16819      */
16820     isExpanded : function(){
16821         return this.list.isVisible();
16822     },
16823
16824     /**
16825      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16826      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16827      * @param {String} value The data value of the item to select
16828      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16829      * selected item if it is not currently in view (defaults to true)
16830      * @return {Boolean} True if the value matched an item in the list, else false
16831      */
16832     selectByValue : function(v, scrollIntoView){
16833         if(v !== undefined && v !== null){
16834             var r = this.findRecord(this.valueField || this.displayField, v);
16835             if(r){
16836                 this.select(this.store.indexOf(r), scrollIntoView);
16837                 return true;
16838             }
16839         }
16840         return false;
16841     },
16842
16843     /**
16844      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16845      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16846      * @param {Number} index The zero-based index of the list item to select
16847      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16848      * selected item if it is not currently in view (defaults to true)
16849      */
16850     select : function(index, scrollIntoView){
16851         this.selectedIndex = index;
16852         this.view.select(index);
16853         if(scrollIntoView !== false){
16854             var el = this.view.getNode(index);
16855             /*
16856              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16857              */
16858             if(el){
16859                 this.list.scrollChildIntoView(el, false);
16860             }
16861         }
16862     },
16863
16864     // private
16865     selectNext : function(){
16866         var ct = this.store.getCount();
16867         if(ct > 0){
16868             if(this.selectedIndex == -1){
16869                 this.select(0);
16870             }else if(this.selectedIndex < ct-1){
16871                 this.select(this.selectedIndex+1);
16872             }
16873         }
16874     },
16875
16876     // private
16877     selectPrev : function(){
16878         var ct = this.store.getCount();
16879         if(ct > 0){
16880             if(this.selectedIndex == -1){
16881                 this.select(0);
16882             }else if(this.selectedIndex != 0){
16883                 this.select(this.selectedIndex-1);
16884             }
16885         }
16886     },
16887
16888     // private
16889     onKeyUp : function(e){
16890         if(this.editable !== false && !e.isSpecialKey()){
16891             this.lastKey = e.getKey();
16892             this.dqTask.delay(this.queryDelay);
16893         }
16894     },
16895
16896     // private
16897     validateBlur : function(){
16898         return !this.list || !this.list.isVisible();   
16899     },
16900
16901     // private
16902     initQuery : function(){
16903         
16904         var v = this.getRawValue();
16905         
16906         if(this.tickable && this.editable){
16907             v = this.tickableInputEl().getValue();
16908         }
16909         
16910         this.doQuery(v);
16911     },
16912
16913     // private
16914     doForce : function(){
16915         if(this.inputEl().dom.value.length > 0){
16916             this.inputEl().dom.value =
16917                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16918              
16919         }
16920     },
16921
16922     /**
16923      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16924      * query allowing the query action to be canceled if needed.
16925      * @param {String} query The SQL query to execute
16926      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16927      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16928      * saved in the current store (defaults to false)
16929      */
16930     doQuery : function(q, forceAll){
16931         
16932         if(q === undefined || q === null){
16933             q = '';
16934         }
16935         var qe = {
16936             query: q,
16937             forceAll: forceAll,
16938             combo: this,
16939             cancel:false
16940         };
16941         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16942             return false;
16943         }
16944         q = qe.query;
16945         
16946         forceAll = qe.forceAll;
16947         if(forceAll === true || (q.length >= this.minChars)){
16948             
16949             this.hasQuery = true;
16950             
16951             if(this.lastQuery != q || this.alwaysQuery){
16952                 this.lastQuery = q;
16953                 if(this.mode == 'local'){
16954                     this.selectedIndex = -1;
16955                     if(forceAll){
16956                         this.store.clearFilter();
16957                     }else{
16958                         
16959                         if(this.specialFilter){
16960                             this.fireEvent('specialfilter', this);
16961                             this.onLoad();
16962                             return;
16963                         }
16964                         
16965                         this.store.filter(this.displayField, q);
16966                     }
16967                     
16968                     this.store.fireEvent("datachanged", this.store);
16969                     
16970                     this.onLoad();
16971                     
16972                     
16973                 }else{
16974                     
16975                     this.store.baseParams[this.queryParam] = q;
16976                     
16977                     var options = {params : this.getParams(q)};
16978                     
16979                     if(this.loadNext){
16980                         options.add = true;
16981                         options.params.start = this.page * this.pageSize;
16982                     }
16983                     
16984                     this.store.load(options);
16985                     
16986                     /*
16987                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16988                      *  we should expand the list on onLoad
16989                      *  so command out it
16990                      */
16991 //                    this.expand();
16992                 }
16993             }else{
16994                 this.selectedIndex = -1;
16995                 this.onLoad();   
16996             }
16997         }
16998         
16999         this.loadNext = false;
17000     },
17001     
17002     // private
17003     getParams : function(q){
17004         var p = {};
17005         //p[this.queryParam] = q;
17006         
17007         if(this.pageSize){
17008             p.start = 0;
17009             p.limit = this.pageSize;
17010         }
17011         return p;
17012     },
17013
17014     /**
17015      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17016      */
17017     collapse : function(){
17018         if(!this.isExpanded()){
17019             return;
17020         }
17021         
17022         this.list.hide();
17023         
17024         this.hasFocus = false;
17025         
17026         if(this.tickable){
17027             this.okBtn.hide();
17028             this.cancelBtn.hide();
17029             this.trigger.show();
17030             
17031             if(this.editable){
17032                 this.tickableInputEl().dom.value = '';
17033                 this.tickableInputEl().blur();
17034             }
17035             
17036         }
17037         
17038         Roo.get(document).un('mousedown', this.collapseIf, this);
17039         Roo.get(document).un('mousewheel', this.collapseIf, this);
17040         if (!this.editable) {
17041             Roo.get(document).un('keydown', this.listKeyPress, this);
17042         }
17043         this.fireEvent('collapse', this);
17044         
17045         this.validate();
17046     },
17047
17048     // private
17049     collapseIf : function(e){
17050         var in_combo  = e.within(this.el);
17051         var in_list =  e.within(this.list);
17052         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17053         
17054         if (in_combo || in_list || is_list) {
17055             //e.stopPropagation();
17056             return;
17057         }
17058         
17059         if(this.tickable){
17060             this.onTickableFooterButtonClick(e, false, false);
17061         }
17062
17063         this.collapse();
17064         
17065     },
17066
17067     /**
17068      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17069      */
17070     expand : function(){
17071        
17072         if(this.isExpanded() || !this.hasFocus){
17073             return;
17074         }
17075         
17076         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17077         this.list.setWidth(lw);
17078         
17079         Roo.log('expand');
17080         
17081         this.list.show();
17082         
17083         this.restrictHeight();
17084         
17085         if(this.tickable){
17086             
17087             this.tickItems = Roo.apply([], this.item);
17088             
17089             this.okBtn.show();
17090             this.cancelBtn.show();
17091             this.trigger.hide();
17092             
17093             if(this.editable){
17094                 this.tickableInputEl().focus();
17095             }
17096             
17097         }
17098         
17099         Roo.get(document).on('mousedown', this.collapseIf, this);
17100         Roo.get(document).on('mousewheel', this.collapseIf, this);
17101         if (!this.editable) {
17102             Roo.get(document).on('keydown', this.listKeyPress, this);
17103         }
17104         
17105         this.fireEvent('expand', this);
17106     },
17107
17108     // private
17109     // Implements the default empty TriggerField.onTriggerClick function
17110     onTriggerClick : function(e)
17111     {
17112         Roo.log('trigger click');
17113         
17114         if(this.disabled || !this.triggerList){
17115             return;
17116         }
17117         
17118         this.page = 0;
17119         this.loadNext = false;
17120         
17121         if(this.isExpanded()){
17122             this.collapse();
17123             if (!this.blockFocus) {
17124                 this.inputEl().focus();
17125             }
17126             
17127         }else {
17128             this.hasFocus = true;
17129             if(this.triggerAction == 'all') {
17130                 this.doQuery(this.allQuery, true);
17131             } else {
17132                 this.doQuery(this.getRawValue());
17133             }
17134             if (!this.blockFocus) {
17135                 this.inputEl().focus();
17136             }
17137         }
17138     },
17139     
17140     onTickableTriggerClick : function(e)
17141     {
17142         if(this.disabled){
17143             return;
17144         }
17145         
17146         this.page = 0;
17147         this.loadNext = false;
17148         this.hasFocus = true;
17149         
17150         if(this.triggerAction == 'all') {
17151             this.doQuery(this.allQuery, true);
17152         } else {
17153             this.doQuery(this.getRawValue());
17154         }
17155     },
17156     
17157     onSearchFieldClick : function(e)
17158     {
17159         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17160             this.onTickableFooterButtonClick(e, false, false);
17161             return;
17162         }
17163         
17164         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17165             return;
17166         }
17167         
17168         this.page = 0;
17169         this.loadNext = false;
17170         this.hasFocus = true;
17171         
17172         if(this.triggerAction == 'all') {
17173             this.doQuery(this.allQuery, true);
17174         } else {
17175             this.doQuery(this.getRawValue());
17176         }
17177     },
17178     
17179     listKeyPress : function(e)
17180     {
17181         //Roo.log('listkeypress');
17182         // scroll to first matching element based on key pres..
17183         if (e.isSpecialKey()) {
17184             return false;
17185         }
17186         var k = String.fromCharCode(e.getKey()).toUpperCase();
17187         //Roo.log(k);
17188         var match  = false;
17189         var csel = this.view.getSelectedNodes();
17190         var cselitem = false;
17191         if (csel.length) {
17192             var ix = this.view.indexOf(csel[0]);
17193             cselitem  = this.store.getAt(ix);
17194             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17195                 cselitem = false;
17196             }
17197             
17198         }
17199         
17200         this.store.each(function(v) { 
17201             if (cselitem) {
17202                 // start at existing selection.
17203                 if (cselitem.id == v.id) {
17204                     cselitem = false;
17205                 }
17206                 return true;
17207             }
17208                 
17209             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17210                 match = this.store.indexOf(v);
17211                 return false;
17212             }
17213             return true;
17214         }, this);
17215         
17216         if (match === false) {
17217             return true; // no more action?
17218         }
17219         // scroll to?
17220         this.view.select(match);
17221         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17222         sn.scrollIntoView(sn.dom.parentNode, false);
17223     },
17224     
17225     onViewScroll : function(e, t){
17226         
17227         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){
17228             return;
17229         }
17230         
17231         this.hasQuery = true;
17232         
17233         this.loading = this.list.select('.loading', true).first();
17234         
17235         if(this.loading === null){
17236             this.list.createChild({
17237                 tag: 'div',
17238                 cls: 'loading roo-select2-more-results roo-select2-active',
17239                 html: 'Loading more results...'
17240             });
17241             
17242             this.loading = this.list.select('.loading', true).first();
17243             
17244             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17245             
17246             this.loading.hide();
17247         }
17248         
17249         this.loading.show();
17250         
17251         var _combo = this;
17252         
17253         this.page++;
17254         this.loadNext = true;
17255         
17256         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17257         
17258         return;
17259     },
17260     
17261     addItem : function(o)
17262     {   
17263         var dv = ''; // display value
17264         
17265         if (this.displayField) {
17266             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17267         } else {
17268             // this is an error condition!!!
17269             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17270         }
17271         
17272         if(!dv.length){
17273             return;
17274         }
17275         
17276         var choice = this.choices.createChild({
17277             tag: 'li',
17278             cls: 'roo-select2-search-choice',
17279             cn: [
17280                 {
17281                     tag: 'div',
17282                     html: dv
17283                 },
17284                 {
17285                     tag: 'a',
17286                     href: '#',
17287                     cls: 'roo-select2-search-choice-close fa fa-times',
17288                     tabindex: '-1'
17289                 }
17290             ]
17291             
17292         }, this.searchField);
17293         
17294         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17295         
17296         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17297         
17298         this.item.push(o);
17299         
17300         this.lastData = o;
17301         
17302         this.syncValue();
17303         
17304         this.inputEl().dom.value = '';
17305         
17306         this.validate();
17307     },
17308     
17309     onRemoveItem : function(e, _self, o)
17310     {
17311         e.preventDefault();
17312         
17313         this.lastItem = Roo.apply([], this.item);
17314         
17315         var index = this.item.indexOf(o.data) * 1;
17316         
17317         if( index < 0){
17318             Roo.log('not this item?!');
17319             return;
17320         }
17321         
17322         this.item.splice(index, 1);
17323         o.item.remove();
17324         
17325         this.syncValue();
17326         
17327         this.fireEvent('remove', this, e);
17328         
17329         this.validate();
17330         
17331     },
17332     
17333     syncValue : function()
17334     {
17335         if(!this.item.length){
17336             this.clearValue();
17337             return;
17338         }
17339             
17340         var value = [];
17341         var _this = this;
17342         Roo.each(this.item, function(i){
17343             if(_this.valueField){
17344                 value.push(i[_this.valueField]);
17345                 return;
17346             }
17347
17348             value.push(i);
17349         });
17350
17351         this.value = value.join(',');
17352
17353         if(this.hiddenField){
17354             this.hiddenField.dom.value = this.value;
17355         }
17356         
17357         this.store.fireEvent("datachanged", this.store);
17358         
17359         this.validate();
17360     },
17361     
17362     clearItem : function()
17363     {
17364         if(!this.multiple){
17365             return;
17366         }
17367         
17368         this.item = [];
17369         
17370         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17371            c.remove();
17372         });
17373         
17374         this.syncValue();
17375         
17376         this.validate();
17377         
17378         if(this.tickable && !Roo.isTouch){
17379             this.view.refresh();
17380         }
17381     },
17382     
17383     inputEl: function ()
17384     {
17385         if(Roo.isIOS && this.useNativeIOS){
17386             return this.el.select('select.roo-ios-select', true).first();
17387         }
17388         
17389         if(Roo.isTouch && this.mobileTouchView){
17390             return this.el.select('input.form-control',true).first();
17391         }
17392         
17393         if(this.tickable){
17394             return this.searchField;
17395         }
17396         
17397         return this.el.select('input.form-control',true).first();
17398     },
17399     
17400     onTickableFooterButtonClick : function(e, btn, el)
17401     {
17402         e.preventDefault();
17403         
17404         this.lastItem = Roo.apply([], this.item);
17405         
17406         if(btn && btn.name == 'cancel'){
17407             this.tickItems = Roo.apply([], this.item);
17408             this.collapse();
17409             return;
17410         }
17411         
17412         this.clearItem();
17413         
17414         var _this = this;
17415         
17416         Roo.each(this.tickItems, function(o){
17417             _this.addItem(o);
17418         });
17419         
17420         this.collapse();
17421         
17422     },
17423     
17424     validate : function()
17425     {
17426         if(this.getVisibilityEl().hasClass('hidden')){
17427             return true;
17428         }
17429         
17430         var v = this.getRawValue();
17431         
17432         if(this.multiple){
17433             v = this.getValue();
17434         }
17435         
17436         if(this.disabled || this.allowBlank || v.length){
17437             this.markValid();
17438             return true;
17439         }
17440         
17441         this.markInvalid();
17442         return false;
17443     },
17444     
17445     tickableInputEl : function()
17446     {
17447         if(!this.tickable || !this.editable){
17448             return this.inputEl();
17449         }
17450         
17451         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17452     },
17453     
17454     
17455     getAutoCreateTouchView : function()
17456     {
17457         var id = Roo.id();
17458         
17459         var cfg = {
17460             cls: 'form-group' //input-group
17461         };
17462         
17463         var input =  {
17464             tag: 'input',
17465             id : id,
17466             type : this.inputType,
17467             cls : 'form-control x-combo-noedit',
17468             autocomplete: 'new-password',
17469             placeholder : this.placeholder || '',
17470             readonly : true
17471         };
17472         
17473         if (this.name) {
17474             input.name = this.name;
17475         }
17476         
17477         if (this.size) {
17478             input.cls += ' input-' + this.size;
17479         }
17480         
17481         if (this.disabled) {
17482             input.disabled = true;
17483         }
17484         
17485         var inputblock = {
17486             cls : 'roo-combobox-wrap',
17487             cn : [
17488                 input
17489             ]
17490         };
17491         
17492         if(this.before){
17493             inputblock.cls += ' input-group';
17494             
17495             inputblock.cn.unshift({
17496                 tag :'span',
17497                 cls : 'input-group-addon input-group-prepend input-group-text',
17498                 html : this.before
17499             });
17500         }
17501         
17502         if(this.removable && !this.multiple){
17503             inputblock.cls += ' roo-removable';
17504             
17505             inputblock.cn.push({
17506                 tag: 'button',
17507                 html : 'x',
17508                 cls : 'roo-combo-removable-btn close'
17509             });
17510         }
17511
17512         if(this.hasFeedback && !this.allowBlank){
17513             
17514             inputblock.cls += ' has-feedback';
17515             
17516             inputblock.cn.push({
17517                 tag: 'span',
17518                 cls: 'glyphicon form-control-feedback'
17519             });
17520             
17521         }
17522         
17523         if (this.after) {
17524             
17525             inputblock.cls += (this.before) ? '' : ' input-group';
17526             
17527             inputblock.cn.push({
17528                 tag :'span',
17529                 cls : 'input-group-addon input-group-append input-group-text',
17530                 html : this.after
17531             });
17532         }
17533
17534         
17535         var ibwrap = inputblock;
17536         
17537         if(this.multiple){
17538             ibwrap = {
17539                 tag: 'ul',
17540                 cls: 'roo-select2-choices',
17541                 cn:[
17542                     {
17543                         tag: 'li',
17544                         cls: 'roo-select2-search-field',
17545                         cn: [
17546
17547                             inputblock
17548                         ]
17549                     }
17550                 ]
17551             };
17552         
17553             
17554         }
17555         
17556         var combobox = {
17557             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17558             cn: [
17559                 {
17560                     tag: 'input',
17561                     type : 'hidden',
17562                     cls: 'form-hidden-field'
17563                 },
17564                 ibwrap
17565             ]
17566         };
17567         
17568         if(!this.multiple && this.showToggleBtn){
17569             
17570             var caret = {
17571                 cls: 'caret'
17572             };
17573             
17574             if (this.caret != false) {
17575                 caret = {
17576                      tag: 'i',
17577                      cls: 'fa fa-' + this.caret
17578                 };
17579                 
17580             }
17581             
17582             combobox.cn.push({
17583                 tag :'span',
17584                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17585                 cn : [
17586                     Roo.bootstrap.version == 3 ? caret : '',
17587                     {
17588                         tag: 'span',
17589                         cls: 'combobox-clear',
17590                         cn  : [
17591                             {
17592                                 tag : 'i',
17593                                 cls: 'icon-remove'
17594                             }
17595                         ]
17596                     }
17597                 ]
17598
17599             })
17600         }
17601         
17602         if(this.multiple){
17603             combobox.cls += ' roo-select2-container-multi';
17604         }
17605         
17606         var required =  this.allowBlank ?  {
17607                     tag : 'i',
17608                     style: 'display: none'
17609                 } : {
17610                    tag : 'i',
17611                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17612                    tooltip : 'This field is required'
17613                 };
17614         
17615         var align = this.labelAlign || this.parentLabelAlign();
17616         
17617         if (align ==='left' && this.fieldLabel.length) {
17618
17619             cfg.cn = [
17620                 required,
17621                 {
17622                     tag: 'label',
17623                     cls : 'control-label col-form-label',
17624                     html : this.fieldLabel
17625
17626                 },
17627                 {
17628                     cls : 'roo-combobox-wrap ', 
17629                     cn: [
17630                         combobox
17631                     ]
17632                 }
17633             ];
17634             
17635             var labelCfg = cfg.cn[1];
17636             var contentCfg = cfg.cn[2];
17637             
17638
17639             if(this.indicatorpos == 'right'){
17640                 cfg.cn = [
17641                     {
17642                         tag: 'label',
17643                         'for' :  id,
17644                         cls : 'control-label col-form-label',
17645                         cn : [
17646                             {
17647                                 tag : 'span',
17648                                 html : this.fieldLabel
17649                             },
17650                             required
17651                         ]
17652                     },
17653                     {
17654                         cls : "roo-combobox-wrap ",
17655                         cn: [
17656                             combobox
17657                         ]
17658                     }
17659
17660                 ];
17661                 
17662                 labelCfg = cfg.cn[0];
17663                 contentCfg = cfg.cn[1];
17664             }
17665             
17666            
17667             
17668             if(this.labelWidth > 12){
17669                 labelCfg.style = "width: " + this.labelWidth + 'px';
17670             }
17671            
17672             if(this.labelWidth < 13 && this.labelmd == 0){
17673                 this.labelmd = this.labelWidth;
17674             }
17675             
17676             if(this.labellg > 0){
17677                 labelCfg.cls += ' col-lg-' + this.labellg;
17678                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17679             }
17680             
17681             if(this.labelmd > 0){
17682                 labelCfg.cls += ' col-md-' + this.labelmd;
17683                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17684             }
17685             
17686             if(this.labelsm > 0){
17687                 labelCfg.cls += ' col-sm-' + this.labelsm;
17688                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17689             }
17690             
17691             if(this.labelxs > 0){
17692                 labelCfg.cls += ' col-xs-' + this.labelxs;
17693                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17694             }
17695                 
17696                 
17697         } else if ( this.fieldLabel.length) {
17698             cfg.cn = [
17699                required,
17700                 {
17701                     tag: 'label',
17702                     cls : 'control-label',
17703                     html : this.fieldLabel
17704
17705                 },
17706                 {
17707                     cls : '', 
17708                     cn: [
17709                         combobox
17710                     ]
17711                 }
17712             ];
17713             
17714             if(this.indicatorpos == 'right'){
17715                 cfg.cn = [
17716                     {
17717                         tag: 'label',
17718                         cls : 'control-label',
17719                         html : this.fieldLabel,
17720                         cn : [
17721                             required
17722                         ]
17723                     },
17724                     {
17725                         cls : '', 
17726                         cn: [
17727                             combobox
17728                         ]
17729                     }
17730                 ];
17731             }
17732         } else {
17733             cfg.cn = combobox;    
17734         }
17735         
17736         
17737         var settings = this;
17738         
17739         ['xs','sm','md','lg'].map(function(size){
17740             if (settings[size]) {
17741                 cfg.cls += ' col-' + size + '-' + settings[size];
17742             }
17743         });
17744         
17745         return cfg;
17746     },
17747     
17748     initTouchView : function()
17749     {
17750         this.renderTouchView();
17751         
17752         this.touchViewEl.on('scroll', function(){
17753             this.el.dom.scrollTop = 0;
17754         }, this);
17755         
17756         this.originalValue = this.getValue();
17757         
17758         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17759         
17760         this.inputEl().on("click", this.showTouchView, this);
17761         if (this.triggerEl) {
17762             this.triggerEl.on("click", this.showTouchView, this);
17763         }
17764         
17765         
17766         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17767         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17768         
17769         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17770         
17771         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17772         this.store.on('load', this.onTouchViewLoad, this);
17773         this.store.on('loadexception', this.onTouchViewLoadException, this);
17774         
17775         if(this.hiddenName){
17776             
17777             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17778             
17779             this.hiddenField.dom.value =
17780                 this.hiddenValue !== undefined ? this.hiddenValue :
17781                 this.value !== undefined ? this.value : '';
17782         
17783             this.el.dom.removeAttribute('name');
17784             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17785         }
17786         
17787         if(this.multiple){
17788             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17789             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17790         }
17791         
17792         if(this.removable && !this.multiple){
17793             var close = this.closeTriggerEl();
17794             if(close){
17795                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17796                 close.on('click', this.removeBtnClick, this, close);
17797             }
17798         }
17799         /*
17800          * fix the bug in Safari iOS8
17801          */
17802         this.inputEl().on("focus", function(e){
17803             document.activeElement.blur();
17804         }, this);
17805         
17806         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17807         
17808         return;
17809         
17810         
17811     },
17812     
17813     renderTouchView : function()
17814     {
17815         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17816         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17817         
17818         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17819         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17820         
17821         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17822         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823         this.touchViewBodyEl.setStyle('overflow', 'auto');
17824         
17825         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17826         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17827         
17828         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17829         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17830         
17831     },
17832     
17833     showTouchView : function()
17834     {
17835         if(this.disabled){
17836             return;
17837         }
17838         
17839         this.touchViewHeaderEl.hide();
17840
17841         if(this.modalTitle.length){
17842             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17843             this.touchViewHeaderEl.show();
17844         }
17845
17846         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17847         this.touchViewEl.show();
17848
17849         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17850         
17851         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17852         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17853
17854         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17855
17856         if(this.modalTitle.length){
17857             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17858         }
17859         
17860         this.touchViewBodyEl.setHeight(bodyHeight);
17861
17862         if(this.animate){
17863             var _this = this;
17864             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17865         }else{
17866             this.touchViewEl.addClass(['in','show']);
17867         }
17868         
17869         if(this._touchViewMask){
17870             Roo.get(document.body).addClass("x-body-masked");
17871             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17872             this._touchViewMask.setStyle('z-index', 10000);
17873             this._touchViewMask.addClass('show');
17874         }
17875         
17876         this.doTouchViewQuery();
17877         
17878     },
17879     
17880     hideTouchView : function()
17881     {
17882         this.touchViewEl.removeClass(['in','show']);
17883
17884         if(this.animate){
17885             var _this = this;
17886             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17887         }else{
17888             this.touchViewEl.setStyle('display', 'none');
17889         }
17890         
17891         if(this._touchViewMask){
17892             this._touchViewMask.removeClass('show');
17893             Roo.get(document.body).removeClass("x-body-masked");
17894         }
17895     },
17896     
17897     setTouchViewValue : function()
17898     {
17899         if(this.multiple){
17900             this.clearItem();
17901         
17902             var _this = this;
17903
17904             Roo.each(this.tickItems, function(o){
17905                 this.addItem(o);
17906             }, this);
17907         }
17908         
17909         this.hideTouchView();
17910     },
17911     
17912     doTouchViewQuery : function()
17913     {
17914         var qe = {
17915             query: '',
17916             forceAll: true,
17917             combo: this,
17918             cancel:false
17919         };
17920         
17921         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17922             return false;
17923         }
17924         
17925         if(!this.alwaysQuery || this.mode == 'local'){
17926             this.onTouchViewLoad();
17927             return;
17928         }
17929         
17930         this.store.load();
17931     },
17932     
17933     onTouchViewBeforeLoad : function(combo,opts)
17934     {
17935         return;
17936     },
17937
17938     // private
17939     onTouchViewLoad : function()
17940     {
17941         if(this.store.getCount() < 1){
17942             this.onTouchViewEmptyResults();
17943             return;
17944         }
17945         
17946         this.clearTouchView();
17947         
17948         var rawValue = this.getRawValue();
17949         
17950         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17951         
17952         this.tickItems = [];
17953         
17954         this.store.data.each(function(d, rowIndex){
17955             var row = this.touchViewListGroup.createChild(template);
17956             
17957             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17958                 row.addClass(d.data.cls);
17959             }
17960             
17961             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17962                 var cfg = {
17963                     data : d.data,
17964                     html : d.data[this.displayField]
17965                 };
17966                 
17967                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17968                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17969                 }
17970             }
17971             row.removeClass('selected');
17972             if(!this.multiple && this.valueField &&
17973                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17974             {
17975                 // radio buttons..
17976                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17977                 row.addClass('selected');
17978             }
17979             
17980             if(this.multiple && this.valueField &&
17981                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17982             {
17983                 
17984                 // checkboxes...
17985                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17986                 this.tickItems.push(d.data);
17987             }
17988             
17989             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17990             
17991         }, this);
17992         
17993         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17994         
17995         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17996
17997         if(this.modalTitle.length){
17998             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17999         }
18000
18001         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18002         
18003         if(this.mobile_restrict_height && listHeight < bodyHeight){
18004             this.touchViewBodyEl.setHeight(listHeight);
18005         }
18006         
18007         var _this = this;
18008         
18009         if(firstChecked && listHeight > bodyHeight){
18010             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18011         }
18012         
18013     },
18014     
18015     onTouchViewLoadException : function()
18016     {
18017         this.hideTouchView();
18018     },
18019     
18020     onTouchViewEmptyResults : function()
18021     {
18022         this.clearTouchView();
18023         
18024         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18025         
18026         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18027         
18028     },
18029     
18030     clearTouchView : function()
18031     {
18032         this.touchViewListGroup.dom.innerHTML = '';
18033     },
18034     
18035     onTouchViewClick : function(e, el, o)
18036     {
18037         e.preventDefault();
18038         
18039         var row = o.row;
18040         var rowIndex = o.rowIndex;
18041         
18042         var r = this.store.getAt(rowIndex);
18043         
18044         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18045             
18046             if(!this.multiple){
18047                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18048                     c.dom.removeAttribute('checked');
18049                 }, this);
18050
18051                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18052
18053                 this.setFromData(r.data);
18054
18055                 var close = this.closeTriggerEl();
18056
18057                 if(close){
18058                     close.show();
18059                 }
18060
18061                 this.hideTouchView();
18062
18063                 this.fireEvent('select', this, r, rowIndex);
18064
18065                 return;
18066             }
18067
18068             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18069                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18070                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18071                 return;
18072             }
18073
18074             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18075             this.addItem(r.data);
18076             this.tickItems.push(r.data);
18077         }
18078     },
18079     
18080     getAutoCreateNativeIOS : function()
18081     {
18082         var cfg = {
18083             cls: 'form-group' //input-group,
18084         };
18085         
18086         var combobox =  {
18087             tag: 'select',
18088             cls : 'roo-ios-select'
18089         };
18090         
18091         if (this.name) {
18092             combobox.name = this.name;
18093         }
18094         
18095         if (this.disabled) {
18096             combobox.disabled = true;
18097         }
18098         
18099         var settings = this;
18100         
18101         ['xs','sm','md','lg'].map(function(size){
18102             if (settings[size]) {
18103                 cfg.cls += ' col-' + size + '-' + settings[size];
18104             }
18105         });
18106         
18107         cfg.cn = combobox;
18108         
18109         return cfg;
18110         
18111     },
18112     
18113     initIOSView : function()
18114     {
18115         this.store.on('load', this.onIOSViewLoad, this);
18116         
18117         return;
18118     },
18119     
18120     onIOSViewLoad : function()
18121     {
18122         if(this.store.getCount() < 1){
18123             return;
18124         }
18125         
18126         this.clearIOSView();
18127         
18128         if(this.allowBlank) {
18129             
18130             var default_text = '-- SELECT --';
18131             
18132             if(this.placeholder.length){
18133                 default_text = this.placeholder;
18134             }
18135             
18136             if(this.emptyTitle.length){
18137                 default_text += ' - ' + this.emptyTitle + ' -';
18138             }
18139             
18140             var opt = this.inputEl().createChild({
18141                 tag: 'option',
18142                 value : 0,
18143                 html : default_text
18144             });
18145             
18146             var o = {};
18147             o[this.valueField] = 0;
18148             o[this.displayField] = default_text;
18149             
18150             this.ios_options.push({
18151                 data : o,
18152                 el : opt
18153             });
18154             
18155         }
18156         
18157         this.store.data.each(function(d, rowIndex){
18158             
18159             var html = '';
18160             
18161             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18162                 html = d.data[this.displayField];
18163             }
18164             
18165             var value = '';
18166             
18167             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18168                 value = d.data[this.valueField];
18169             }
18170             
18171             var option = {
18172                 tag: 'option',
18173                 value : value,
18174                 html : html
18175             };
18176             
18177             if(this.value == d.data[this.valueField]){
18178                 option['selected'] = true;
18179             }
18180             
18181             var opt = this.inputEl().createChild(option);
18182             
18183             this.ios_options.push({
18184                 data : d.data,
18185                 el : opt
18186             });
18187             
18188         }, this);
18189         
18190         this.inputEl().on('change', function(){
18191            this.fireEvent('select', this);
18192         }, this);
18193         
18194     },
18195     
18196     clearIOSView: function()
18197     {
18198         this.inputEl().dom.innerHTML = '';
18199         
18200         this.ios_options = [];
18201     },
18202     
18203     setIOSValue: function(v)
18204     {
18205         this.value = v;
18206         
18207         if(!this.ios_options){
18208             return;
18209         }
18210         
18211         Roo.each(this.ios_options, function(opts){
18212            
18213            opts.el.dom.removeAttribute('selected');
18214            
18215            if(opts.data[this.valueField] != v){
18216                return;
18217            }
18218            
18219            opts.el.dom.setAttribute('selected', true);
18220            
18221         }, this);
18222     }
18223
18224     /** 
18225     * @cfg {Boolean} grow 
18226     * @hide 
18227     */
18228     /** 
18229     * @cfg {Number} growMin 
18230     * @hide 
18231     */
18232     /** 
18233     * @cfg {Number} growMax 
18234     * @hide 
18235     */
18236     /**
18237      * @hide
18238      * @method autoSize
18239      */
18240 });
18241
18242 Roo.apply(Roo.bootstrap.ComboBox,  {
18243     
18244     header : {
18245         tag: 'div',
18246         cls: 'modal-header',
18247         cn: [
18248             {
18249                 tag: 'h4',
18250                 cls: 'modal-title'
18251             }
18252         ]
18253     },
18254     
18255     body : {
18256         tag: 'div',
18257         cls: 'modal-body',
18258         cn: [
18259             {
18260                 tag: 'ul',
18261                 cls: 'list-group'
18262             }
18263         ]
18264     },
18265     
18266     listItemRadio : {
18267         tag: 'li',
18268         cls: 'list-group-item',
18269         cn: [
18270             {
18271                 tag: 'span',
18272                 cls: 'roo-combobox-list-group-item-value'
18273             },
18274             {
18275                 tag: 'div',
18276                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18277                 cn: [
18278                     {
18279                         tag: 'input',
18280                         type: 'radio'
18281                     },
18282                     {
18283                         tag: 'label'
18284                     }
18285                 ]
18286             }
18287         ]
18288     },
18289     
18290     listItemCheckbox : {
18291         tag: 'li',
18292         cls: 'list-group-item',
18293         cn: [
18294             {
18295                 tag: 'span',
18296                 cls: 'roo-combobox-list-group-item-value'
18297             },
18298             {
18299                 tag: 'div',
18300                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18301                 cn: [
18302                     {
18303                         tag: 'input',
18304                         type: 'checkbox'
18305                     },
18306                     {
18307                         tag: 'label'
18308                     }
18309                 ]
18310             }
18311         ]
18312     },
18313     
18314     emptyResult : {
18315         tag: 'div',
18316         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18317     },
18318     
18319     footer : {
18320         tag: 'div',
18321         cls: 'modal-footer',
18322         cn: [
18323             {
18324                 tag: 'div',
18325                 cls: 'row',
18326                 cn: [
18327                     {
18328                         tag: 'div',
18329                         cls: 'col-xs-6 text-left',
18330                         cn: {
18331                             tag: 'button',
18332                             cls: 'btn btn-danger roo-touch-view-cancel',
18333                             html: 'Cancel'
18334                         }
18335                     },
18336                     {
18337                         tag: 'div',
18338                         cls: 'col-xs-6 text-right',
18339                         cn: {
18340                             tag: 'button',
18341                             cls: 'btn btn-success roo-touch-view-ok',
18342                             html: 'OK'
18343                         }
18344                     }
18345                 ]
18346             }
18347         ]
18348         
18349     }
18350 });
18351
18352 Roo.apply(Roo.bootstrap.ComboBox,  {
18353     
18354     touchViewTemplate : {
18355         tag: 'div',
18356         cls: 'modal fade roo-combobox-touch-view',
18357         cn: [
18358             {
18359                 tag: 'div',
18360                 cls: 'modal-dialog',
18361                 style : 'position:fixed', // we have to fix position....
18362                 cn: [
18363                     {
18364                         tag: 'div',
18365                         cls: 'modal-content',
18366                         cn: [
18367                             Roo.bootstrap.ComboBox.header,
18368                             Roo.bootstrap.ComboBox.body,
18369                             Roo.bootstrap.ComboBox.footer
18370                         ]
18371                     }
18372                 ]
18373             }
18374         ]
18375     }
18376 });/*
18377  * Based on:
18378  * Ext JS Library 1.1.1
18379  * Copyright(c) 2006-2007, Ext JS, LLC.
18380  *
18381  * Originally Released Under LGPL - original licence link has changed is not relivant.
18382  *
18383  * Fork - LGPL
18384  * <script type="text/javascript">
18385  */
18386
18387 /**
18388  * @class Roo.View
18389  * @extends Roo.util.Observable
18390  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18391  * This class also supports single and multi selection modes. <br>
18392  * Create a data model bound view:
18393  <pre><code>
18394  var store = new Roo.data.Store(...);
18395
18396  var view = new Roo.View({
18397     el : "my-element",
18398     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18399  
18400     singleSelect: true,
18401     selectedClass: "ydataview-selected",
18402     store: store
18403  });
18404
18405  // listen for node click?
18406  view.on("click", function(vw, index, node, e){
18407  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18408  });
18409
18410  // load XML data
18411  dataModel.load("foobar.xml");
18412  </code></pre>
18413  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18414  * <br><br>
18415  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18416  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18417  * 
18418  * Note: old style constructor is still suported (container, template, config)
18419  * 
18420  * @constructor
18421  * Create a new View
18422  * @param {Object} config The config object
18423  * 
18424  */
18425 Roo.View = function(config, depreciated_tpl, depreciated_config){
18426     
18427     this.parent = false;
18428     
18429     if (typeof(depreciated_tpl) == 'undefined') {
18430         // new way.. - universal constructor.
18431         Roo.apply(this, config);
18432         this.el  = Roo.get(this.el);
18433     } else {
18434         // old format..
18435         this.el  = Roo.get(config);
18436         this.tpl = depreciated_tpl;
18437         Roo.apply(this, depreciated_config);
18438     }
18439     this.wrapEl  = this.el.wrap().wrap();
18440     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18441     
18442     
18443     if(typeof(this.tpl) == "string"){
18444         this.tpl = new Roo.Template(this.tpl);
18445     } else {
18446         // support xtype ctors..
18447         this.tpl = new Roo.factory(this.tpl, Roo);
18448     }
18449     
18450     
18451     this.tpl.compile();
18452     
18453     /** @private */
18454     this.addEvents({
18455         /**
18456          * @event beforeclick
18457          * Fires before a click is processed. Returns false to cancel the default action.
18458          * @param {Roo.View} this
18459          * @param {Number} index The index of the target node
18460          * @param {HTMLElement} node The target node
18461          * @param {Roo.EventObject} e The raw event object
18462          */
18463             "beforeclick" : true,
18464         /**
18465          * @event click
18466          * Fires when a template node is clicked.
18467          * @param {Roo.View} this
18468          * @param {Number} index The index of the target node
18469          * @param {HTMLElement} node The target node
18470          * @param {Roo.EventObject} e The raw event object
18471          */
18472             "click" : true,
18473         /**
18474          * @event dblclick
18475          * Fires when a template node is double clicked.
18476          * @param {Roo.View} this
18477          * @param {Number} index The index of the target node
18478          * @param {HTMLElement} node The target node
18479          * @param {Roo.EventObject} e The raw event object
18480          */
18481             "dblclick" : true,
18482         /**
18483          * @event contextmenu
18484          * Fires when a template node is right clicked.
18485          * @param {Roo.View} this
18486          * @param {Number} index The index of the target node
18487          * @param {HTMLElement} node The target node
18488          * @param {Roo.EventObject} e The raw event object
18489          */
18490             "contextmenu" : true,
18491         /**
18492          * @event selectionchange
18493          * Fires when the selected nodes change.
18494          * @param {Roo.View} this
18495          * @param {Array} selections Array of the selected nodes
18496          */
18497             "selectionchange" : true,
18498     
18499         /**
18500          * @event beforeselect
18501          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18502          * @param {Roo.View} this
18503          * @param {HTMLElement} node The node to be selected
18504          * @param {Array} selections Array of currently selected nodes
18505          */
18506             "beforeselect" : true,
18507         /**
18508          * @event preparedata
18509          * Fires on every row to render, to allow you to change the data.
18510          * @param {Roo.View} this
18511          * @param {Object} data to be rendered (change this)
18512          */
18513           "preparedata" : true
18514           
18515           
18516         });
18517
18518
18519
18520     this.el.on({
18521         "click": this.onClick,
18522         "dblclick": this.onDblClick,
18523         "contextmenu": this.onContextMenu,
18524         scope:this
18525     });
18526
18527     this.selections = [];
18528     this.nodes = [];
18529     this.cmp = new Roo.CompositeElementLite([]);
18530     if(this.store){
18531         this.store = Roo.factory(this.store, Roo.data);
18532         this.setStore(this.store, true);
18533     }
18534     
18535     if ( this.footer && this.footer.xtype) {
18536            
18537          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18538         
18539         this.footer.dataSource = this.store;
18540         this.footer.container = fctr;
18541         this.footer = Roo.factory(this.footer, Roo);
18542         fctr.insertFirst(this.el);
18543         
18544         // this is a bit insane - as the paging toolbar seems to detach the el..
18545 //        dom.parentNode.parentNode.parentNode
18546          // they get detached?
18547     }
18548     
18549     
18550     Roo.View.superclass.constructor.call(this);
18551     
18552     
18553 };
18554
18555 Roo.extend(Roo.View, Roo.util.Observable, {
18556     
18557      /**
18558      * @cfg {Roo.data.Store} store Data store to load data from.
18559      */
18560     store : false,
18561     
18562     /**
18563      * @cfg {String|Roo.Element} el The container element.
18564      */
18565     el : '',
18566     
18567     /**
18568      * @cfg {String|Roo.Template} tpl The template used by this View 
18569      */
18570     tpl : false,
18571     /**
18572      * @cfg {String} dataName the named area of the template to use as the data area
18573      *                          Works with domtemplates roo-name="name"
18574      */
18575     dataName: false,
18576     /**
18577      * @cfg {String} selectedClass The css class to add to selected nodes
18578      */
18579     selectedClass : "x-view-selected",
18580      /**
18581      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18582      */
18583     emptyText : "",
18584     
18585     /**
18586      * @cfg {String} text to display on mask (default Loading)
18587      */
18588     mask : false,
18589     /**
18590      * @cfg {Boolean} multiSelect Allow multiple selection
18591      */
18592     multiSelect : false,
18593     /**
18594      * @cfg {Boolean} singleSelect Allow single selection
18595      */
18596     singleSelect:  false,
18597     
18598     /**
18599      * @cfg {Boolean} toggleSelect - selecting 
18600      */
18601     toggleSelect : false,
18602     
18603     /**
18604      * @cfg {Boolean} tickable - selecting 
18605      */
18606     tickable : false,
18607     
18608     /**
18609      * Returns the element this view is bound to.
18610      * @return {Roo.Element}
18611      */
18612     getEl : function(){
18613         return this.wrapEl;
18614     },
18615     
18616     
18617
18618     /**
18619      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18620      */
18621     refresh : function(){
18622         //Roo.log('refresh');
18623         var t = this.tpl;
18624         
18625         // if we are using something like 'domtemplate', then
18626         // the what gets used is:
18627         // t.applySubtemplate(NAME, data, wrapping data..)
18628         // the outer template then get' applied with
18629         //     the store 'extra data'
18630         // and the body get's added to the
18631         //      roo-name="data" node?
18632         //      <span class='roo-tpl-{name}'></span> ?????
18633         
18634         
18635         
18636         this.clearSelections();
18637         this.el.update("");
18638         var html = [];
18639         var records = this.store.getRange();
18640         if(records.length < 1) {
18641             
18642             // is this valid??  = should it render a template??
18643             
18644             this.el.update(this.emptyText);
18645             return;
18646         }
18647         var el = this.el;
18648         if (this.dataName) {
18649             this.el.update(t.apply(this.store.meta)); //????
18650             el = this.el.child('.roo-tpl-' + this.dataName);
18651         }
18652         
18653         for(var i = 0, len = records.length; i < len; i++){
18654             var data = this.prepareData(records[i].data, i, records[i]);
18655             this.fireEvent("preparedata", this, data, i, records[i]);
18656             
18657             var d = Roo.apply({}, data);
18658             
18659             if(this.tickable){
18660                 Roo.apply(d, {'roo-id' : Roo.id()});
18661                 
18662                 var _this = this;
18663             
18664                 Roo.each(this.parent.item, function(item){
18665                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18666                         return;
18667                     }
18668                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18669                 });
18670             }
18671             
18672             html[html.length] = Roo.util.Format.trim(
18673                 this.dataName ?
18674                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18675                     t.apply(d)
18676             );
18677         }
18678         
18679         
18680         
18681         el.update(html.join(""));
18682         this.nodes = el.dom.childNodes;
18683         this.updateIndexes(0);
18684     },
18685     
18686
18687     /**
18688      * Function to override to reformat the data that is sent to
18689      * the template for each node.
18690      * DEPRICATED - use the preparedata event handler.
18691      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18692      * a JSON object for an UpdateManager bound view).
18693      */
18694     prepareData : function(data, index, record)
18695     {
18696         this.fireEvent("preparedata", this, data, index, record);
18697         return data;
18698     },
18699
18700     onUpdate : function(ds, record){
18701         // Roo.log('on update');   
18702         this.clearSelections();
18703         var index = this.store.indexOf(record);
18704         var n = this.nodes[index];
18705         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18706         n.parentNode.removeChild(n);
18707         this.updateIndexes(index, index);
18708     },
18709
18710     
18711     
18712 // --------- FIXME     
18713     onAdd : function(ds, records, index)
18714     {
18715         //Roo.log(['on Add', ds, records, index] );        
18716         this.clearSelections();
18717         if(this.nodes.length == 0){
18718             this.refresh();
18719             return;
18720         }
18721         var n = this.nodes[index];
18722         for(var i = 0, len = records.length; i < len; i++){
18723             var d = this.prepareData(records[i].data, i, records[i]);
18724             if(n){
18725                 this.tpl.insertBefore(n, d);
18726             }else{
18727                 
18728                 this.tpl.append(this.el, d);
18729             }
18730         }
18731         this.updateIndexes(index);
18732     },
18733
18734     onRemove : function(ds, record, index){
18735        // Roo.log('onRemove');
18736         this.clearSelections();
18737         var el = this.dataName  ?
18738             this.el.child('.roo-tpl-' + this.dataName) :
18739             this.el; 
18740         
18741         el.dom.removeChild(this.nodes[index]);
18742         this.updateIndexes(index);
18743     },
18744
18745     /**
18746      * Refresh an individual node.
18747      * @param {Number} index
18748      */
18749     refreshNode : function(index){
18750         this.onUpdate(this.store, this.store.getAt(index));
18751     },
18752
18753     updateIndexes : function(startIndex, endIndex){
18754         var ns = this.nodes;
18755         startIndex = startIndex || 0;
18756         endIndex = endIndex || ns.length - 1;
18757         for(var i = startIndex; i <= endIndex; i++){
18758             ns[i].nodeIndex = i;
18759         }
18760     },
18761
18762     /**
18763      * Changes the data store this view uses and refresh the view.
18764      * @param {Store} store
18765      */
18766     setStore : function(store, initial){
18767         if(!initial && this.store){
18768             this.store.un("datachanged", this.refresh);
18769             this.store.un("add", this.onAdd);
18770             this.store.un("remove", this.onRemove);
18771             this.store.un("update", this.onUpdate);
18772             this.store.un("clear", this.refresh);
18773             this.store.un("beforeload", this.onBeforeLoad);
18774             this.store.un("load", this.onLoad);
18775             this.store.un("loadexception", this.onLoad);
18776         }
18777         if(store){
18778           
18779             store.on("datachanged", this.refresh, this);
18780             store.on("add", this.onAdd, this);
18781             store.on("remove", this.onRemove, this);
18782             store.on("update", this.onUpdate, this);
18783             store.on("clear", this.refresh, this);
18784             store.on("beforeload", this.onBeforeLoad, this);
18785             store.on("load", this.onLoad, this);
18786             store.on("loadexception", this.onLoad, this);
18787         }
18788         
18789         if(store){
18790             this.refresh();
18791         }
18792     },
18793     /**
18794      * onbeforeLoad - masks the loading area.
18795      *
18796      */
18797     onBeforeLoad : function(store,opts)
18798     {
18799          //Roo.log('onBeforeLoad');   
18800         if (!opts.add) {
18801             this.el.update("");
18802         }
18803         this.el.mask(this.mask ? this.mask : "Loading" ); 
18804     },
18805     onLoad : function ()
18806     {
18807         this.el.unmask();
18808     },
18809     
18810
18811     /**
18812      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18813      * @param {HTMLElement} node
18814      * @return {HTMLElement} The template node
18815      */
18816     findItemFromChild : function(node){
18817         var el = this.dataName  ?
18818             this.el.child('.roo-tpl-' + this.dataName,true) :
18819             this.el.dom; 
18820         
18821         if(!node || node.parentNode == el){
18822                     return node;
18823             }
18824             var p = node.parentNode;
18825             while(p && p != el){
18826             if(p.parentNode == el){
18827                 return p;
18828             }
18829             p = p.parentNode;
18830         }
18831             return null;
18832     },
18833
18834     /** @ignore */
18835     onClick : function(e){
18836         var item = this.findItemFromChild(e.getTarget());
18837         if(item){
18838             var index = this.indexOf(item);
18839             if(this.onItemClick(item, index, e) !== false){
18840                 this.fireEvent("click", this, index, item, e);
18841             }
18842         }else{
18843             this.clearSelections();
18844         }
18845     },
18846
18847     /** @ignore */
18848     onContextMenu : function(e){
18849         var item = this.findItemFromChild(e.getTarget());
18850         if(item){
18851             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18852         }
18853     },
18854
18855     /** @ignore */
18856     onDblClick : function(e){
18857         var item = this.findItemFromChild(e.getTarget());
18858         if(item){
18859             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18860         }
18861     },
18862
18863     onItemClick : function(item, index, e)
18864     {
18865         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18866             return false;
18867         }
18868         if (this.toggleSelect) {
18869             var m = this.isSelected(item) ? 'unselect' : 'select';
18870             //Roo.log(m);
18871             var _t = this;
18872             _t[m](item, true, false);
18873             return true;
18874         }
18875         if(this.multiSelect || this.singleSelect){
18876             if(this.multiSelect && e.shiftKey && this.lastSelection){
18877                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18878             }else{
18879                 this.select(item, this.multiSelect && e.ctrlKey);
18880                 this.lastSelection = item;
18881             }
18882             
18883             if(!this.tickable){
18884                 e.preventDefault();
18885             }
18886             
18887         }
18888         return true;
18889     },
18890
18891     /**
18892      * Get the number of selected nodes.
18893      * @return {Number}
18894      */
18895     getSelectionCount : function(){
18896         return this.selections.length;
18897     },
18898
18899     /**
18900      * Get the currently selected nodes.
18901      * @return {Array} An array of HTMLElements
18902      */
18903     getSelectedNodes : function(){
18904         return this.selections;
18905     },
18906
18907     /**
18908      * Get the indexes of the selected nodes.
18909      * @return {Array}
18910      */
18911     getSelectedIndexes : function(){
18912         var indexes = [], s = this.selections;
18913         for(var i = 0, len = s.length; i < len; i++){
18914             indexes.push(s[i].nodeIndex);
18915         }
18916         return indexes;
18917     },
18918
18919     /**
18920      * Clear all selections
18921      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18922      */
18923     clearSelections : function(suppressEvent){
18924         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18925             this.cmp.elements = this.selections;
18926             this.cmp.removeClass(this.selectedClass);
18927             this.selections = [];
18928             if(!suppressEvent){
18929                 this.fireEvent("selectionchange", this, this.selections);
18930             }
18931         }
18932     },
18933
18934     /**
18935      * Returns true if the passed node is selected
18936      * @param {HTMLElement/Number} node The node or node index
18937      * @return {Boolean}
18938      */
18939     isSelected : function(node){
18940         var s = this.selections;
18941         if(s.length < 1){
18942             return false;
18943         }
18944         node = this.getNode(node);
18945         return s.indexOf(node) !== -1;
18946     },
18947
18948     /**
18949      * Selects nodes.
18950      * @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
18951      * @param {Boolean} keepExisting (optional) true to keep existing selections
18952      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18953      */
18954     select : function(nodeInfo, keepExisting, suppressEvent){
18955         if(nodeInfo instanceof Array){
18956             if(!keepExisting){
18957                 this.clearSelections(true);
18958             }
18959             for(var i = 0, len = nodeInfo.length; i < len; i++){
18960                 this.select(nodeInfo[i], true, true);
18961             }
18962             return;
18963         } 
18964         var node = this.getNode(nodeInfo);
18965         if(!node || this.isSelected(node)){
18966             return; // already selected.
18967         }
18968         if(!keepExisting){
18969             this.clearSelections(true);
18970         }
18971         
18972         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18973             Roo.fly(node).addClass(this.selectedClass);
18974             this.selections.push(node);
18975             if(!suppressEvent){
18976                 this.fireEvent("selectionchange", this, this.selections);
18977             }
18978         }
18979         
18980         
18981     },
18982       /**
18983      * Unselects nodes.
18984      * @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
18985      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18986      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18987      */
18988     unselect : function(nodeInfo, keepExisting, suppressEvent)
18989     {
18990         if(nodeInfo instanceof Array){
18991             Roo.each(this.selections, function(s) {
18992                 this.unselect(s, nodeInfo);
18993             }, this);
18994             return;
18995         }
18996         var node = this.getNode(nodeInfo);
18997         if(!node || !this.isSelected(node)){
18998             //Roo.log("not selected");
18999             return; // not selected.
19000         }
19001         // fireevent???
19002         var ns = [];
19003         Roo.each(this.selections, function(s) {
19004             if (s == node ) {
19005                 Roo.fly(node).removeClass(this.selectedClass);
19006
19007                 return;
19008             }
19009             ns.push(s);
19010         },this);
19011         
19012         this.selections= ns;
19013         this.fireEvent("selectionchange", this, this.selections);
19014     },
19015
19016     /**
19017      * Gets a template node.
19018      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19019      * @return {HTMLElement} The node or null if it wasn't found
19020      */
19021     getNode : function(nodeInfo){
19022         if(typeof nodeInfo == "string"){
19023             return document.getElementById(nodeInfo);
19024         }else if(typeof nodeInfo == "number"){
19025             return this.nodes[nodeInfo];
19026         }
19027         return nodeInfo;
19028     },
19029
19030     /**
19031      * Gets a range template nodes.
19032      * @param {Number} startIndex
19033      * @param {Number} endIndex
19034      * @return {Array} An array of nodes
19035      */
19036     getNodes : function(start, end){
19037         var ns = this.nodes;
19038         start = start || 0;
19039         end = typeof end == "undefined" ? ns.length - 1 : end;
19040         var nodes = [];
19041         if(start <= end){
19042             for(var i = start; i <= end; i++){
19043                 nodes.push(ns[i]);
19044             }
19045         } else{
19046             for(var i = start; i >= end; i--){
19047                 nodes.push(ns[i]);
19048             }
19049         }
19050         return nodes;
19051     },
19052
19053     /**
19054      * Finds the index of the passed node
19055      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19056      * @return {Number} The index of the node or -1
19057      */
19058     indexOf : function(node){
19059         node = this.getNode(node);
19060         if(typeof node.nodeIndex == "number"){
19061             return node.nodeIndex;
19062         }
19063         var ns = this.nodes;
19064         for(var i = 0, len = ns.length; i < len; i++){
19065             if(ns[i] == node){
19066                 return i;
19067             }
19068         }
19069         return -1;
19070     }
19071 });
19072 /*
19073  * - LGPL
19074  *
19075  * based on jquery fullcalendar
19076  * 
19077  */
19078
19079 Roo.bootstrap = Roo.bootstrap || {};
19080 /**
19081  * @class Roo.bootstrap.Calendar
19082  * @extends Roo.bootstrap.Component
19083  * Bootstrap Calendar class
19084  * @cfg {Boolean} loadMask (true|false) default false
19085  * @cfg {Object} header generate the user specific header of the calendar, default false
19086
19087  * @constructor
19088  * Create a new Container
19089  * @param {Object} config The config object
19090  */
19091
19092
19093
19094 Roo.bootstrap.Calendar = function(config){
19095     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19096      this.addEvents({
19097         /**
19098              * @event select
19099              * Fires when a date is selected
19100              * @param {DatePicker} this
19101              * @param {Date} date The selected date
19102              */
19103         'select': true,
19104         /**
19105              * @event monthchange
19106              * Fires when the displayed month changes 
19107              * @param {DatePicker} this
19108              * @param {Date} date The selected month
19109              */
19110         'monthchange': true,
19111         /**
19112              * @event evententer
19113              * Fires when mouse over an event
19114              * @param {Calendar} this
19115              * @param {event} Event
19116              */
19117         'evententer': true,
19118         /**
19119              * @event eventleave
19120              * Fires when the mouse leaves an
19121              * @param {Calendar} this
19122              * @param {event}
19123              */
19124         'eventleave': true,
19125         /**
19126              * @event eventclick
19127              * Fires when the mouse click an
19128              * @param {Calendar} this
19129              * @param {event}
19130              */
19131         'eventclick': true
19132         
19133     });
19134
19135 };
19136
19137 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19138     
19139      /**
19140      * @cfg {Number} startDay
19141      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19142      */
19143     startDay : 0,
19144     
19145     loadMask : false,
19146     
19147     header : false,
19148       
19149     getAutoCreate : function(){
19150         
19151         
19152         var fc_button = function(name, corner, style, content ) {
19153             return Roo.apply({},{
19154                 tag : 'span',
19155                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19156                          (corner.length ?
19157                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19158                             ''
19159                         ),
19160                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19161                 unselectable: 'on'
19162             });
19163         };
19164         
19165         var header = {};
19166         
19167         if(!this.header){
19168             header = {
19169                 tag : 'table',
19170                 cls : 'fc-header',
19171                 style : 'width:100%',
19172                 cn : [
19173                     {
19174                         tag: 'tr',
19175                         cn : [
19176                             {
19177                                 tag : 'td',
19178                                 cls : 'fc-header-left',
19179                                 cn : [
19180                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19181                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19182                                     { tag: 'span', cls: 'fc-header-space' },
19183                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19184
19185
19186                                 ]
19187                             },
19188
19189                             {
19190                                 tag : 'td',
19191                                 cls : 'fc-header-center',
19192                                 cn : [
19193                                     {
19194                                         tag: 'span',
19195                                         cls: 'fc-header-title',
19196                                         cn : {
19197                                             tag: 'H2',
19198                                             html : 'month / year'
19199                                         }
19200                                     }
19201
19202                                 ]
19203                             },
19204                             {
19205                                 tag : 'td',
19206                                 cls : 'fc-header-right',
19207                                 cn : [
19208                               /*      fc_button('month', 'left', '', 'month' ),
19209                                     fc_button('week', '', '', 'week' ),
19210                                     fc_button('day', 'right', '', 'day' )
19211                                 */    
19212
19213                                 ]
19214                             }
19215
19216                         ]
19217                     }
19218                 ]
19219             };
19220         }
19221         
19222         header = this.header;
19223         
19224        
19225         var cal_heads = function() {
19226             var ret = [];
19227             // fixme - handle this.
19228             
19229             for (var i =0; i < Date.dayNames.length; i++) {
19230                 var d = Date.dayNames[i];
19231                 ret.push({
19232                     tag: 'th',
19233                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19234                     html : d.substring(0,3)
19235                 });
19236                 
19237             }
19238             ret[0].cls += ' fc-first';
19239             ret[6].cls += ' fc-last';
19240             return ret;
19241         };
19242         var cal_cell = function(n) {
19243             return  {
19244                 tag: 'td',
19245                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19246                 cn : [
19247                     {
19248                         cn : [
19249                             {
19250                                 cls: 'fc-day-number',
19251                                 html: 'D'
19252                             },
19253                             {
19254                                 cls: 'fc-day-content',
19255                              
19256                                 cn : [
19257                                      {
19258                                         style: 'position: relative;' // height: 17px;
19259                                     }
19260                                 ]
19261                             }
19262                             
19263                             
19264                         ]
19265                     }
19266                 ]
19267                 
19268             }
19269         };
19270         var cal_rows = function() {
19271             
19272             var ret = [];
19273             for (var r = 0; r < 6; r++) {
19274                 var row= {
19275                     tag : 'tr',
19276                     cls : 'fc-week',
19277                     cn : []
19278                 };
19279                 
19280                 for (var i =0; i < Date.dayNames.length; i++) {
19281                     var d = Date.dayNames[i];
19282                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19283
19284                 }
19285                 row.cn[0].cls+=' fc-first';
19286                 row.cn[0].cn[0].style = 'min-height:90px';
19287                 row.cn[6].cls+=' fc-last';
19288                 ret.push(row);
19289                 
19290             }
19291             ret[0].cls += ' fc-first';
19292             ret[4].cls += ' fc-prev-last';
19293             ret[5].cls += ' fc-last';
19294             return ret;
19295             
19296         };
19297         
19298         var cal_table = {
19299             tag: 'table',
19300             cls: 'fc-border-separate',
19301             style : 'width:100%',
19302             cellspacing  : 0,
19303             cn : [
19304                 { 
19305                     tag: 'thead',
19306                     cn : [
19307                         { 
19308                             tag: 'tr',
19309                             cls : 'fc-first fc-last',
19310                             cn : cal_heads()
19311                         }
19312                     ]
19313                 },
19314                 { 
19315                     tag: 'tbody',
19316                     cn : cal_rows()
19317                 }
19318                   
19319             ]
19320         };
19321          
19322          var cfg = {
19323             cls : 'fc fc-ltr',
19324             cn : [
19325                 header,
19326                 {
19327                     cls : 'fc-content',
19328                     style : "position: relative;",
19329                     cn : [
19330                         {
19331                             cls : 'fc-view fc-view-month fc-grid',
19332                             style : 'position: relative',
19333                             unselectable : 'on',
19334                             cn : [
19335                                 {
19336                                     cls : 'fc-event-container',
19337                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19338                                 },
19339                                 cal_table
19340                             ]
19341                         }
19342                     ]
19343     
19344                 }
19345            ] 
19346             
19347         };
19348         
19349          
19350         
19351         return cfg;
19352     },
19353     
19354     
19355     initEvents : function()
19356     {
19357         if(!this.store){
19358             throw "can not find store for calendar";
19359         }
19360         
19361         var mark = {
19362             tag: "div",
19363             cls:"x-dlg-mask",
19364             style: "text-align:center",
19365             cn: [
19366                 {
19367                     tag: "div",
19368                     style: "background-color:white;width:50%;margin:250 auto",
19369                     cn: [
19370                         {
19371                             tag: "img",
19372                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19373                         },
19374                         {
19375                             tag: "span",
19376                             html: "Loading"
19377                         }
19378                         
19379                     ]
19380                 }
19381             ]
19382         };
19383         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19384         
19385         var size = this.el.select('.fc-content', true).first().getSize();
19386         this.maskEl.setSize(size.width, size.height);
19387         this.maskEl.enableDisplayMode("block");
19388         if(!this.loadMask){
19389             this.maskEl.hide();
19390         }
19391         
19392         this.store = Roo.factory(this.store, Roo.data);
19393         this.store.on('load', this.onLoad, this);
19394         this.store.on('beforeload', this.onBeforeLoad, this);
19395         
19396         this.resize();
19397         
19398         this.cells = this.el.select('.fc-day',true);
19399         //Roo.log(this.cells);
19400         this.textNodes = this.el.query('.fc-day-number');
19401         this.cells.addClassOnOver('fc-state-hover');
19402         
19403         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19404         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19405         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19406         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19407         
19408         this.on('monthchange', this.onMonthChange, this);
19409         
19410         this.update(new Date().clearTime());
19411     },
19412     
19413     resize : function() {
19414         var sz  = this.el.getSize();
19415         
19416         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19417         this.el.select('.fc-day-content div',true).setHeight(34);
19418     },
19419     
19420     
19421     // private
19422     showPrevMonth : function(e){
19423         this.update(this.activeDate.add("mo", -1));
19424     },
19425     showToday : function(e){
19426         this.update(new Date().clearTime());
19427     },
19428     // private
19429     showNextMonth : function(e){
19430         this.update(this.activeDate.add("mo", 1));
19431     },
19432
19433     // private
19434     showPrevYear : function(){
19435         this.update(this.activeDate.add("y", -1));
19436     },
19437
19438     // private
19439     showNextYear : function(){
19440         this.update(this.activeDate.add("y", 1));
19441     },
19442
19443     
19444    // private
19445     update : function(date)
19446     {
19447         var vd = this.activeDate;
19448         this.activeDate = date;
19449 //        if(vd && this.el){
19450 //            var t = date.getTime();
19451 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19452 //                Roo.log('using add remove');
19453 //                
19454 //                this.fireEvent('monthchange', this, date);
19455 //                
19456 //                this.cells.removeClass("fc-state-highlight");
19457 //                this.cells.each(function(c){
19458 //                   if(c.dateValue == t){
19459 //                       c.addClass("fc-state-highlight");
19460 //                       setTimeout(function(){
19461 //                            try{c.dom.firstChild.focus();}catch(e){}
19462 //                       }, 50);
19463 //                       return false;
19464 //                   }
19465 //                   return true;
19466 //                });
19467 //                return;
19468 //            }
19469 //        }
19470         
19471         var days = date.getDaysInMonth();
19472         
19473         var firstOfMonth = date.getFirstDateOfMonth();
19474         var startingPos = firstOfMonth.getDay()-this.startDay;
19475         
19476         if(startingPos < this.startDay){
19477             startingPos += 7;
19478         }
19479         
19480         var pm = date.add(Date.MONTH, -1);
19481         var prevStart = pm.getDaysInMonth()-startingPos;
19482 //        
19483         this.cells = this.el.select('.fc-day',true);
19484         this.textNodes = this.el.query('.fc-day-number');
19485         this.cells.addClassOnOver('fc-state-hover');
19486         
19487         var cells = this.cells.elements;
19488         var textEls = this.textNodes;
19489         
19490         Roo.each(cells, function(cell){
19491             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19492         });
19493         
19494         days += startingPos;
19495
19496         // convert everything to numbers so it's fast
19497         var day = 86400000;
19498         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19499         //Roo.log(d);
19500         //Roo.log(pm);
19501         //Roo.log(prevStart);
19502         
19503         var today = new Date().clearTime().getTime();
19504         var sel = date.clearTime().getTime();
19505         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19506         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19507         var ddMatch = this.disabledDatesRE;
19508         var ddText = this.disabledDatesText;
19509         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19510         var ddaysText = this.disabledDaysText;
19511         var format = this.format;
19512         
19513         var setCellClass = function(cal, cell){
19514             cell.row = 0;
19515             cell.events = [];
19516             cell.more = [];
19517             //Roo.log('set Cell Class');
19518             cell.title = "";
19519             var t = d.getTime();
19520             
19521             //Roo.log(d);
19522             
19523             cell.dateValue = t;
19524             if(t == today){
19525                 cell.className += " fc-today";
19526                 cell.className += " fc-state-highlight";
19527                 cell.title = cal.todayText;
19528             }
19529             if(t == sel){
19530                 // disable highlight in other month..
19531                 //cell.className += " fc-state-highlight";
19532                 
19533             }
19534             // disabling
19535             if(t < min) {
19536                 cell.className = " fc-state-disabled";
19537                 cell.title = cal.minText;
19538                 return;
19539             }
19540             if(t > max) {
19541                 cell.className = " fc-state-disabled";
19542                 cell.title = cal.maxText;
19543                 return;
19544             }
19545             if(ddays){
19546                 if(ddays.indexOf(d.getDay()) != -1){
19547                     cell.title = ddaysText;
19548                     cell.className = " fc-state-disabled";
19549                 }
19550             }
19551             if(ddMatch && format){
19552                 var fvalue = d.dateFormat(format);
19553                 if(ddMatch.test(fvalue)){
19554                     cell.title = ddText.replace("%0", fvalue);
19555                     cell.className = " fc-state-disabled";
19556                 }
19557             }
19558             
19559             if (!cell.initialClassName) {
19560                 cell.initialClassName = cell.dom.className;
19561             }
19562             
19563             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19564         };
19565
19566         var i = 0;
19567         
19568         for(; i < startingPos; i++) {
19569             textEls[i].innerHTML = (++prevStart);
19570             d.setDate(d.getDate()+1);
19571             
19572             cells[i].className = "fc-past fc-other-month";
19573             setCellClass(this, cells[i]);
19574         }
19575         
19576         var intDay = 0;
19577         
19578         for(; i < days; i++){
19579             intDay = i - startingPos + 1;
19580             textEls[i].innerHTML = (intDay);
19581             d.setDate(d.getDate()+1);
19582             
19583             cells[i].className = ''; // "x-date-active";
19584             setCellClass(this, cells[i]);
19585         }
19586         var extraDays = 0;
19587         
19588         for(; i < 42; i++) {
19589             textEls[i].innerHTML = (++extraDays);
19590             d.setDate(d.getDate()+1);
19591             
19592             cells[i].className = "fc-future fc-other-month";
19593             setCellClass(this, cells[i]);
19594         }
19595         
19596         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19597         
19598         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19599         
19600         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19601         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19602         
19603         if(totalRows != 6){
19604             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19605             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19606         }
19607         
19608         this.fireEvent('monthchange', this, date);
19609         
19610         
19611         /*
19612         if(!this.internalRender){
19613             var main = this.el.dom.firstChild;
19614             var w = main.offsetWidth;
19615             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19616             Roo.fly(main).setWidth(w);
19617             this.internalRender = true;
19618             // opera does not respect the auto grow header center column
19619             // then, after it gets a width opera refuses to recalculate
19620             // without a second pass
19621             if(Roo.isOpera && !this.secondPass){
19622                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19623                 this.secondPass = true;
19624                 this.update.defer(10, this, [date]);
19625             }
19626         }
19627         */
19628         
19629     },
19630     
19631     findCell : function(dt) {
19632         dt = dt.clearTime().getTime();
19633         var ret = false;
19634         this.cells.each(function(c){
19635             //Roo.log("check " +c.dateValue + '?=' + dt);
19636             if(c.dateValue == dt){
19637                 ret = c;
19638                 return false;
19639             }
19640             return true;
19641         });
19642         
19643         return ret;
19644     },
19645     
19646     findCells : function(ev) {
19647         var s = ev.start.clone().clearTime().getTime();
19648        // Roo.log(s);
19649         var e= ev.end.clone().clearTime().getTime();
19650        // Roo.log(e);
19651         var ret = [];
19652         this.cells.each(function(c){
19653              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19654             
19655             if(c.dateValue > e){
19656                 return ;
19657             }
19658             if(c.dateValue < s){
19659                 return ;
19660             }
19661             ret.push(c);
19662         });
19663         
19664         return ret;    
19665     },
19666     
19667 //    findBestRow: function(cells)
19668 //    {
19669 //        var ret = 0;
19670 //        
19671 //        for (var i =0 ; i < cells.length;i++) {
19672 //            ret  = Math.max(cells[i].rows || 0,ret);
19673 //        }
19674 //        return ret;
19675 //        
19676 //    },
19677     
19678     
19679     addItem : function(ev)
19680     {
19681         // look for vertical location slot in
19682         var cells = this.findCells(ev);
19683         
19684 //        ev.row = this.findBestRow(cells);
19685         
19686         // work out the location.
19687         
19688         var crow = false;
19689         var rows = [];
19690         for(var i =0; i < cells.length; i++) {
19691             
19692             cells[i].row = cells[0].row;
19693             
19694             if(i == 0){
19695                 cells[i].row = cells[i].row + 1;
19696             }
19697             
19698             if (!crow) {
19699                 crow = {
19700                     start : cells[i],
19701                     end :  cells[i]
19702                 };
19703                 continue;
19704             }
19705             if (crow.start.getY() == cells[i].getY()) {
19706                 // on same row.
19707                 crow.end = cells[i];
19708                 continue;
19709             }
19710             // different row.
19711             rows.push(crow);
19712             crow = {
19713                 start: cells[i],
19714                 end : cells[i]
19715             };
19716             
19717         }
19718         
19719         rows.push(crow);
19720         ev.els = [];
19721         ev.rows = rows;
19722         ev.cells = cells;
19723         
19724         cells[0].events.push(ev);
19725         
19726         this.calevents.push(ev);
19727     },
19728     
19729     clearEvents: function() {
19730         
19731         if(!this.calevents){
19732             return;
19733         }
19734         
19735         Roo.each(this.cells.elements, function(c){
19736             c.row = 0;
19737             c.events = [];
19738             c.more = [];
19739         });
19740         
19741         Roo.each(this.calevents, function(e) {
19742             Roo.each(e.els, function(el) {
19743                 el.un('mouseenter' ,this.onEventEnter, this);
19744                 el.un('mouseleave' ,this.onEventLeave, this);
19745                 el.remove();
19746             },this);
19747         },this);
19748         
19749         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19750             e.remove();
19751         });
19752         
19753     },
19754     
19755     renderEvents: function()
19756     {   
19757         var _this = this;
19758         
19759         this.cells.each(function(c) {
19760             
19761             if(c.row < 5){
19762                 return;
19763             }
19764             
19765             var ev = c.events;
19766             
19767             var r = 4;
19768             if(c.row != c.events.length){
19769                 r = 4 - (4 - (c.row - c.events.length));
19770             }
19771             
19772             c.events = ev.slice(0, r);
19773             c.more = ev.slice(r);
19774             
19775             if(c.more.length && c.more.length == 1){
19776                 c.events.push(c.more.pop());
19777             }
19778             
19779             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19780             
19781         });
19782             
19783         this.cells.each(function(c) {
19784             
19785             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19786             
19787             
19788             for (var e = 0; e < c.events.length; e++){
19789                 var ev = c.events[e];
19790                 var rows = ev.rows;
19791                 
19792                 for(var i = 0; i < rows.length; i++) {
19793                 
19794                     // how many rows should it span..
19795
19796                     var  cfg = {
19797                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19798                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19799
19800                         unselectable : "on",
19801                         cn : [
19802                             {
19803                                 cls: 'fc-event-inner',
19804                                 cn : [
19805     //                                {
19806     //                                  tag:'span',
19807     //                                  cls: 'fc-event-time',
19808     //                                  html : cells.length > 1 ? '' : ev.time
19809     //                                },
19810                                     {
19811                                       tag:'span',
19812                                       cls: 'fc-event-title',
19813                                       html : String.format('{0}', ev.title)
19814                                     }
19815
19816
19817                                 ]
19818                             },
19819                             {
19820                                 cls: 'ui-resizable-handle ui-resizable-e',
19821                                 html : '&nbsp;&nbsp;&nbsp'
19822                             }
19823
19824                         ]
19825                     };
19826
19827                     if (i == 0) {
19828                         cfg.cls += ' fc-event-start';
19829                     }
19830                     if ((i+1) == rows.length) {
19831                         cfg.cls += ' fc-event-end';
19832                     }
19833
19834                     var ctr = _this.el.select('.fc-event-container',true).first();
19835                     var cg = ctr.createChild(cfg);
19836
19837                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19838                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19839
19840                     var r = (c.more.length) ? 1 : 0;
19841                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19842                     cg.setWidth(ebox.right - sbox.x -2);
19843
19844                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19845                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19846                     cg.on('click', _this.onEventClick, _this, ev);
19847
19848                     ev.els.push(cg);
19849                     
19850                 }
19851                 
19852             }
19853             
19854             
19855             if(c.more.length){
19856                 var  cfg = {
19857                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19858                     style : 'position: absolute',
19859                     unselectable : "on",
19860                     cn : [
19861                         {
19862                             cls: 'fc-event-inner',
19863                             cn : [
19864                                 {
19865                                   tag:'span',
19866                                   cls: 'fc-event-title',
19867                                   html : 'More'
19868                                 }
19869
19870
19871                             ]
19872                         },
19873                         {
19874                             cls: 'ui-resizable-handle ui-resizable-e',
19875                             html : '&nbsp;&nbsp;&nbsp'
19876                         }
19877
19878                     ]
19879                 };
19880
19881                 var ctr = _this.el.select('.fc-event-container',true).first();
19882                 var cg = ctr.createChild(cfg);
19883
19884                 var sbox = c.select('.fc-day-content',true).first().getBox();
19885                 var ebox = c.select('.fc-day-content',true).first().getBox();
19886                 //Roo.log(cg);
19887                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19888                 cg.setWidth(ebox.right - sbox.x -2);
19889
19890                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19891                 
19892             }
19893             
19894         });
19895         
19896         
19897         
19898     },
19899     
19900     onEventEnter: function (e, el,event,d) {
19901         this.fireEvent('evententer', this, el, event);
19902     },
19903     
19904     onEventLeave: function (e, el,event,d) {
19905         this.fireEvent('eventleave', this, el, event);
19906     },
19907     
19908     onEventClick: function (e, el,event,d) {
19909         this.fireEvent('eventclick', this, el, event);
19910     },
19911     
19912     onMonthChange: function () {
19913         this.store.load();
19914     },
19915     
19916     onMoreEventClick: function(e, el, more)
19917     {
19918         var _this = this;
19919         
19920         this.calpopover.placement = 'right';
19921         this.calpopover.setTitle('More');
19922         
19923         this.calpopover.setContent('');
19924         
19925         var ctr = this.calpopover.el.select('.popover-content', true).first();
19926         
19927         Roo.each(more, function(m){
19928             var cfg = {
19929                 cls : 'fc-event-hori fc-event-draggable',
19930                 html : m.title
19931             };
19932             var cg = ctr.createChild(cfg);
19933             
19934             cg.on('click', _this.onEventClick, _this, m);
19935         });
19936         
19937         this.calpopover.show(el);
19938         
19939         
19940     },
19941     
19942     onLoad: function () 
19943     {   
19944         this.calevents = [];
19945         var cal = this;
19946         
19947         if(this.store.getCount() > 0){
19948             this.store.data.each(function(d){
19949                cal.addItem({
19950                     id : d.data.id,
19951                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19952                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19953                     time : d.data.start_time,
19954                     title : d.data.title,
19955                     description : d.data.description,
19956                     venue : d.data.venue
19957                 });
19958             });
19959         }
19960         
19961         this.renderEvents();
19962         
19963         if(this.calevents.length && this.loadMask){
19964             this.maskEl.hide();
19965         }
19966     },
19967     
19968     onBeforeLoad: function()
19969     {
19970         this.clearEvents();
19971         if(this.loadMask){
19972             this.maskEl.show();
19973         }
19974     }
19975 });
19976
19977  
19978  /*
19979  * - LGPL
19980  *
19981  * element
19982  * 
19983  */
19984
19985 /**
19986  * @class Roo.bootstrap.Popover
19987  * @extends Roo.bootstrap.Component
19988  * Bootstrap Popover class
19989  * @cfg {String} html contents of the popover   (or false to use children..)
19990  * @cfg {String} title of popover (or false to hide)
19991  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19992  * @cfg {String} trigger click || hover (or false to trigger manually)
19993  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19994  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19995  *      - if false and it has a 'parent' then it will be automatically added to that element
19996  *      - if string - Roo.get  will be called 
19997  * @cfg {Number} delay - delay before showing
19998  
19999  * @constructor
20000  * Create a new Popover
20001  * @param {Object} config The config object
20002  */
20003
20004 Roo.bootstrap.Popover = function(config){
20005     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20006     
20007     this.addEvents({
20008         // raw events
20009          /**
20010          * @event show
20011          * After the popover show
20012          * 
20013          * @param {Roo.bootstrap.Popover} this
20014          */
20015         "show" : true,
20016         /**
20017          * @event hide
20018          * After the popover hide
20019          * 
20020          * @param {Roo.bootstrap.Popover} this
20021          */
20022         "hide" : true
20023     });
20024 };
20025
20026 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20027     
20028     title: false,
20029     html: false,
20030     
20031     placement : 'right',
20032     trigger : 'hover', // hover
20033     modal : false,
20034     delay : 0,
20035     
20036     over: false,
20037     
20038     can_build_overlaid : false,
20039     
20040     maskEl : false, // the mask element
20041     headerEl : false,
20042     contentEl : false,
20043     alignEl : false, // when show is called with an element - this get's stored.
20044     
20045     getChildContainer : function()
20046     {
20047         return this.contentEl;
20048         
20049     },
20050     getPopoverHeader : function()
20051     {
20052         this.title = true; // flag not to hide it..
20053         this.headerEl.addClass('p-0');
20054         return this.headerEl
20055     },
20056     
20057     
20058     getAutoCreate : function(){
20059          
20060         var cfg = {
20061            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20062            style: 'display:block',
20063            cn : [
20064                 {
20065                     cls : 'arrow'
20066                 },
20067                 {
20068                     cls : 'popover-inner ',
20069                     cn : [
20070                         {
20071                             tag: 'h3',
20072                             cls: 'popover-title popover-header',
20073                             html : this.title === false ? '' : this.title
20074                         },
20075                         {
20076                             cls : 'popover-content popover-body '  + (this.cls || ''),
20077                             html : this.html || ''
20078                         }
20079                     ]
20080                     
20081                 }
20082            ]
20083         };
20084         
20085         return cfg;
20086     },
20087     /**
20088      * @param {string} the title
20089      */
20090     setTitle: function(str)
20091     {
20092         this.title = str;
20093         if (this.el) {
20094             this.headerEl.dom.innerHTML = str;
20095         }
20096         
20097     },
20098     /**
20099      * @param {string} the body content
20100      */
20101     setContent: function(str)
20102     {
20103         this.html = str;
20104         if (this.contentEl) {
20105             this.contentEl.dom.innerHTML = str;
20106         }
20107         
20108     },
20109     // as it get's added to the bottom of the page.
20110     onRender : function(ct, position)
20111     {
20112         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20113         
20114         
20115         
20116         if(!this.el){
20117             var cfg = Roo.apply({},  this.getAutoCreate());
20118             cfg.id = Roo.id();
20119             
20120             if (this.cls) {
20121                 cfg.cls += ' ' + this.cls;
20122             }
20123             if (this.style) {
20124                 cfg.style = this.style;
20125             }
20126             //Roo.log("adding to ");
20127             this.el = Roo.get(document.body).createChild(cfg, position);
20128 //            Roo.log(this.el);
20129         }
20130         
20131         this.contentEl = this.el.select('.popover-content',true).first();
20132         this.headerEl =  this.el.select('.popover-title',true).first();
20133         
20134         var nitems = [];
20135         if(typeof(this.items) != 'undefined'){
20136             var items = this.items;
20137             delete this.items;
20138
20139             for(var i =0;i < items.length;i++) {
20140                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20141             }
20142         }
20143
20144         this.items = nitems;
20145         
20146         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20147         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20148         
20149         
20150         
20151         this.initEvents();
20152     },
20153     
20154     resizeMask : function()
20155     {
20156         this.maskEl.setSize(
20157             Roo.lib.Dom.getViewWidth(true),
20158             Roo.lib.Dom.getViewHeight(true)
20159         );
20160     },
20161     
20162     initEvents : function()
20163     {
20164         
20165         if (!this.modal) { 
20166             Roo.bootstrap.Popover.register(this);
20167         }
20168          
20169         this.arrowEl = this.el.select('.arrow',true).first();
20170         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20171         this.el.enableDisplayMode('block');
20172         this.el.hide();
20173  
20174         
20175         if (this.over === false && !this.parent()) {
20176             return; 
20177         }
20178         if (this.triggers === false) {
20179             return;
20180         }
20181          
20182         // support parent
20183         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20184         var triggers = this.trigger ? this.trigger.split(' ') : [];
20185         Roo.each(triggers, function(trigger) {
20186         
20187             if (trigger == 'click') {
20188                 on_el.on('click', this.toggle, this);
20189             } else if (trigger != 'manual') {
20190                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20191                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20192       
20193                 on_el.on(eventIn  ,this.enter, this);
20194                 on_el.on(eventOut, this.leave, this);
20195             }
20196         }, this);
20197     },
20198     
20199     
20200     // private
20201     timeout : null,
20202     hoverState : null,
20203     
20204     toggle : function () {
20205         this.hoverState == 'in' ? this.leave() : this.enter();
20206     },
20207     
20208     enter : function () {
20209         
20210         clearTimeout(this.timeout);
20211     
20212         this.hoverState = 'in';
20213     
20214         if (!this.delay || !this.delay.show) {
20215             this.show();
20216             return;
20217         }
20218         var _t = this;
20219         this.timeout = setTimeout(function () {
20220             if (_t.hoverState == 'in') {
20221                 _t.show();
20222             }
20223         }, this.delay.show)
20224     },
20225     
20226     leave : function() {
20227         clearTimeout(this.timeout);
20228     
20229         this.hoverState = 'out';
20230     
20231         if (!this.delay || !this.delay.hide) {
20232             this.hide();
20233             return;
20234         }
20235         var _t = this;
20236         this.timeout = setTimeout(function () {
20237             if (_t.hoverState == 'out') {
20238                 _t.hide();
20239             }
20240         }, this.delay.hide)
20241     },
20242     /**
20243      * Show the popover
20244      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20245      * @param {string} (left|right|top|bottom) position
20246      */
20247     show : function (on_el, placement)
20248     {
20249         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20250         on_el = on_el || false; // default to false
20251          
20252         if (!on_el) {
20253             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20254                 on_el = this.parent().el;
20255             } else if (this.over) {
20256                 on_el = Roo.get(this.over);
20257             }
20258             
20259         }
20260         
20261         this.alignEl = Roo.get( on_el );
20262
20263         if (!this.el) {
20264             this.render(document.body);
20265         }
20266         
20267         
20268          
20269         
20270         if (this.title === false) {
20271             this.headerEl.hide();
20272         }
20273         
20274        
20275         this.el.show();
20276         this.el.dom.style.display = 'block';
20277          
20278  
20279         if (this.alignEl) {
20280             this.updatePosition(this.placement, true);
20281              
20282         } else {
20283             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20284             var es = this.el.getSize();
20285             var x = Roo.lib.Dom.getViewWidth()/2;
20286             var y = Roo.lib.Dom.getViewHeight()/2;
20287             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20288             
20289         }
20290
20291         
20292         //var arrow = this.el.select('.arrow',true).first();
20293         //arrow.set(align[2], 
20294         
20295         this.el.addClass('in');
20296         
20297          
20298         
20299         this.hoverState = 'in';
20300         
20301         if (this.modal) {
20302             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20303             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20304             this.maskEl.dom.style.display = 'block';
20305             this.maskEl.addClass('show');
20306         }
20307         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20308  
20309         this.fireEvent('show', this);
20310         
20311     },
20312     /**
20313      * fire this manually after loading a grid in the table for example
20314      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20315      * @param {Boolean} try and move it if we cant get right position.
20316      */
20317     updatePosition : function(placement, try_move)
20318     {
20319         // allow for calling with no parameters
20320         placement = placement   ? placement :  this.placement;
20321         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20322         
20323         this.el.removeClass([
20324             'fade','top','bottom', 'left', 'right','in',
20325             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20326         ]);
20327         this.el.addClass(placement + ' bs-popover-' + placement);
20328         
20329         if (!this.alignEl ) {
20330             return false;
20331         }
20332         
20333         switch (placement) {
20334             case 'right':
20335                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20336                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20337                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20338                     //normal display... or moved up/down.
20339                     this.el.setXY(offset);
20340                     var xy = this.alignEl.getAnchorXY('tr', false);
20341                     xy[0]+=2;xy[1]+=5;
20342                     this.arrowEl.setXY(xy);
20343                     return true;
20344                 }
20345                 // continue through...
20346                 return this.updatePosition('left', false);
20347                 
20348             
20349             case 'left':
20350                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20351                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20352                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20353                     //normal display... or moved up/down.
20354                     this.el.setXY(offset);
20355                     var xy = this.alignEl.getAnchorXY('tl', false);
20356                     xy[0]-=10;xy[1]+=5; // << fix me
20357                     this.arrowEl.setXY(xy);
20358                     return true;
20359                 }
20360                 // call self...
20361                 return this.updatePosition('right', false);
20362             
20363             case 'top':
20364                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20365                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20366                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20367                     //normal display... or moved up/down.
20368                     this.el.setXY(offset);
20369                     var xy = this.alignEl.getAnchorXY('t', false);
20370                     xy[1]-=10; // << fix me
20371                     this.arrowEl.setXY(xy);
20372                     return true;
20373                 }
20374                 // fall through
20375                return this.updatePosition('bottom', false);
20376             
20377             case 'bottom':
20378                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20379                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20380                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20381                     //normal display... or moved up/down.
20382                     this.el.setXY(offset);
20383                     var xy = this.alignEl.getAnchorXY('b', false);
20384                      xy[1]+=2; // << fix me
20385                     this.arrowEl.setXY(xy);
20386                     return true;
20387                 }
20388                 // fall through
20389                 return this.updatePosition('top', false);
20390                 
20391             
20392         }
20393         
20394         
20395         return false;
20396     },
20397     
20398     hide : function()
20399     {
20400         this.el.setXY([0,0]);
20401         this.el.removeClass('in');
20402         this.el.hide();
20403         this.hoverState = null;
20404         this.maskEl.hide(); // always..
20405         this.fireEvent('hide', this);
20406     }
20407     
20408 });
20409
20410
20411 Roo.apply(Roo.bootstrap.Popover, {
20412
20413     alignment : {
20414         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20415         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20416         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20417         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20418     },
20419     
20420     zIndex : 20001,
20421
20422     clickHander : false,
20423     
20424     
20425
20426     onMouseDown : function(e)
20427     {
20428         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20429             /// what is nothing is showing..
20430             this.hideAll();
20431         }
20432          
20433     },
20434     
20435     
20436     popups : [],
20437     
20438     register : function(popup)
20439     {
20440         if (!Roo.bootstrap.Popover.clickHandler) {
20441             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20442         }
20443         // hide other popups.
20444         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20445         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20446         this.hideAll(); //<< why?
20447         //this.popups.push(popup);
20448     },
20449     hideAll : function()
20450     {
20451         this.popups.forEach(function(p) {
20452             p.hide();
20453         });
20454     },
20455     onShow : function() {
20456         Roo.bootstrap.Popover.popups.push(this);
20457     },
20458     onHide : function() {
20459         Roo.bootstrap.Popover.popups.remove(this);
20460     } 
20461
20462 });/*
20463  * - LGPL
20464  *
20465  * Card header - holder for the card header elements.
20466  * 
20467  */
20468
20469 /**
20470  * @class Roo.bootstrap.PopoverNav
20471  * @extends Roo.bootstrap.NavGroup
20472  * Bootstrap Popover header navigation class
20473  * @constructor
20474  * Create a new Popover Header Navigation 
20475  * @param {Object} config The config object
20476  */
20477
20478 Roo.bootstrap.PopoverNav = function(config){
20479     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20480 };
20481
20482 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20483     
20484     
20485     container_method : 'getPopoverHeader' 
20486     
20487      
20488     
20489     
20490    
20491 });
20492
20493  
20494
20495  /*
20496  * - LGPL
20497  *
20498  * Progress
20499  * 
20500  */
20501
20502 /**
20503  * @class Roo.bootstrap.Progress
20504  * @extends Roo.bootstrap.Component
20505  * Bootstrap Progress class
20506  * @cfg {Boolean} striped striped of the progress bar
20507  * @cfg {Boolean} active animated of the progress bar
20508  * 
20509  * 
20510  * @constructor
20511  * Create a new Progress
20512  * @param {Object} config The config object
20513  */
20514
20515 Roo.bootstrap.Progress = function(config){
20516     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20517 };
20518
20519 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20520     
20521     striped : false,
20522     active: false,
20523     
20524     getAutoCreate : function(){
20525         var cfg = {
20526             tag: 'div',
20527             cls: 'progress'
20528         };
20529         
20530         
20531         if(this.striped){
20532             cfg.cls += ' progress-striped';
20533         }
20534       
20535         if(this.active){
20536             cfg.cls += ' active';
20537         }
20538         
20539         
20540         return cfg;
20541     }
20542    
20543 });
20544
20545  
20546
20547  /*
20548  * - LGPL
20549  *
20550  * ProgressBar
20551  * 
20552  */
20553
20554 /**
20555  * @class Roo.bootstrap.ProgressBar
20556  * @extends Roo.bootstrap.Component
20557  * Bootstrap ProgressBar class
20558  * @cfg {Number} aria_valuenow aria-value now
20559  * @cfg {Number} aria_valuemin aria-value min
20560  * @cfg {Number} aria_valuemax aria-value max
20561  * @cfg {String} label label for the progress bar
20562  * @cfg {String} panel (success | info | warning | danger )
20563  * @cfg {String} role role of the progress bar
20564  * @cfg {String} sr_only text
20565  * 
20566  * 
20567  * @constructor
20568  * Create a new ProgressBar
20569  * @param {Object} config The config object
20570  */
20571
20572 Roo.bootstrap.ProgressBar = function(config){
20573     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20574 };
20575
20576 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20577     
20578     aria_valuenow : 0,
20579     aria_valuemin : 0,
20580     aria_valuemax : 100,
20581     label : false,
20582     panel : false,
20583     role : false,
20584     sr_only: false,
20585     
20586     getAutoCreate : function()
20587     {
20588         
20589         var cfg = {
20590             tag: 'div',
20591             cls: 'progress-bar',
20592             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20593         };
20594         
20595         if(this.sr_only){
20596             cfg.cn = {
20597                 tag: 'span',
20598                 cls: 'sr-only',
20599                 html: this.sr_only
20600             }
20601         }
20602         
20603         if(this.role){
20604             cfg.role = this.role;
20605         }
20606         
20607         if(this.aria_valuenow){
20608             cfg['aria-valuenow'] = this.aria_valuenow;
20609         }
20610         
20611         if(this.aria_valuemin){
20612             cfg['aria-valuemin'] = this.aria_valuemin;
20613         }
20614         
20615         if(this.aria_valuemax){
20616             cfg['aria-valuemax'] = this.aria_valuemax;
20617         }
20618         
20619         if(this.label && !this.sr_only){
20620             cfg.html = this.label;
20621         }
20622         
20623         if(this.panel){
20624             cfg.cls += ' progress-bar-' + this.panel;
20625         }
20626         
20627         return cfg;
20628     },
20629     
20630     update : function(aria_valuenow)
20631     {
20632         this.aria_valuenow = aria_valuenow;
20633         
20634         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20635     }
20636    
20637 });
20638
20639  
20640
20641  /*
20642  * - LGPL
20643  *
20644  * column
20645  * 
20646  */
20647
20648 /**
20649  * @class Roo.bootstrap.TabGroup
20650  * @extends Roo.bootstrap.Column
20651  * Bootstrap Column class
20652  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20653  * @cfg {Boolean} carousel true to make the group behave like a carousel
20654  * @cfg {Boolean} bullets show bullets for the panels
20655  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20656  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20657  * @cfg {Boolean} showarrow (true|false) show arrow default true
20658  * 
20659  * @constructor
20660  * Create a new TabGroup
20661  * @param {Object} config The config object
20662  */
20663
20664 Roo.bootstrap.TabGroup = function(config){
20665     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20666     if (!this.navId) {
20667         this.navId = Roo.id();
20668     }
20669     this.tabs = [];
20670     Roo.bootstrap.TabGroup.register(this);
20671     
20672 };
20673
20674 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20675     
20676     carousel : false,
20677     transition : false,
20678     bullets : 0,
20679     timer : 0,
20680     autoslide : false,
20681     slideFn : false,
20682     slideOnTouch : false,
20683     showarrow : true,
20684     
20685     getAutoCreate : function()
20686     {
20687         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20688         
20689         cfg.cls += ' tab-content';
20690         
20691         if (this.carousel) {
20692             cfg.cls += ' carousel slide';
20693             
20694             cfg.cn = [{
20695                cls : 'carousel-inner',
20696                cn : []
20697             }];
20698         
20699             if(this.bullets  && !Roo.isTouch){
20700                 
20701                 var bullets = {
20702                     cls : 'carousel-bullets',
20703                     cn : []
20704                 };
20705                
20706                 if(this.bullets_cls){
20707                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20708                 }
20709                 
20710                 bullets.cn.push({
20711                     cls : 'clear'
20712                 });
20713                 
20714                 cfg.cn[0].cn.push(bullets);
20715             }
20716             
20717             if(this.showarrow){
20718                 cfg.cn[0].cn.push({
20719                     tag : 'div',
20720                     class : 'carousel-arrow',
20721                     cn : [
20722                         {
20723                             tag : 'div',
20724                             class : 'carousel-prev',
20725                             cn : [
20726                                 {
20727                                     tag : 'i',
20728                                     class : 'fa fa-chevron-left'
20729                                 }
20730                             ]
20731                         },
20732                         {
20733                             tag : 'div',
20734                             class : 'carousel-next',
20735                             cn : [
20736                                 {
20737                                     tag : 'i',
20738                                     class : 'fa fa-chevron-right'
20739                                 }
20740                             ]
20741                         }
20742                     ]
20743                 });
20744             }
20745             
20746         }
20747         
20748         return cfg;
20749     },
20750     
20751     initEvents:  function()
20752     {
20753 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20754 //            this.el.on("touchstart", this.onTouchStart, this);
20755 //        }
20756         
20757         if(this.autoslide){
20758             var _this = this;
20759             
20760             this.slideFn = window.setInterval(function() {
20761                 _this.showPanelNext();
20762             }, this.timer);
20763         }
20764         
20765         if(this.showarrow){
20766             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20767             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20768         }
20769         
20770         
20771     },
20772     
20773 //    onTouchStart : function(e, el, o)
20774 //    {
20775 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20776 //            return;
20777 //        }
20778 //        
20779 //        this.showPanelNext();
20780 //    },
20781     
20782     
20783     getChildContainer : function()
20784     {
20785         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20786     },
20787     
20788     /**
20789     * register a Navigation item
20790     * @param {Roo.bootstrap.NavItem} the navitem to add
20791     */
20792     register : function(item)
20793     {
20794         this.tabs.push( item);
20795         item.navId = this.navId; // not really needed..
20796         this.addBullet();
20797     
20798     },
20799     
20800     getActivePanel : function()
20801     {
20802         var r = false;
20803         Roo.each(this.tabs, function(t) {
20804             if (t.active) {
20805                 r = t;
20806                 return false;
20807             }
20808             return null;
20809         });
20810         return r;
20811         
20812     },
20813     getPanelByName : function(n)
20814     {
20815         var r = false;
20816         Roo.each(this.tabs, function(t) {
20817             if (t.tabId == n) {
20818                 r = t;
20819                 return false;
20820             }
20821             return null;
20822         });
20823         return r;
20824     },
20825     indexOfPanel : function(p)
20826     {
20827         var r = false;
20828         Roo.each(this.tabs, function(t,i) {
20829             if (t.tabId == p.tabId) {
20830                 r = i;
20831                 return false;
20832             }
20833             return null;
20834         });
20835         return r;
20836     },
20837     /**
20838      * show a specific panel
20839      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20840      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20841      */
20842     showPanel : function (pan)
20843     {
20844         if(this.transition || typeof(pan) == 'undefined'){
20845             Roo.log("waiting for the transitionend");
20846             return false;
20847         }
20848         
20849         if (typeof(pan) == 'number') {
20850             pan = this.tabs[pan];
20851         }
20852         
20853         if (typeof(pan) == 'string') {
20854             pan = this.getPanelByName(pan);
20855         }
20856         
20857         var cur = this.getActivePanel();
20858         
20859         if(!pan || !cur){
20860             Roo.log('pan or acitve pan is undefined');
20861             return false;
20862         }
20863         
20864         if (pan.tabId == this.getActivePanel().tabId) {
20865             return true;
20866         }
20867         
20868         if (false === cur.fireEvent('beforedeactivate')) {
20869             return false;
20870         }
20871         
20872         if(this.bullets > 0 && !Roo.isTouch){
20873             this.setActiveBullet(this.indexOfPanel(pan));
20874         }
20875         
20876         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20877             
20878             //class="carousel-item carousel-item-next carousel-item-left"
20879             
20880             this.transition = true;
20881             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20882             var lr = dir == 'next' ? 'left' : 'right';
20883             pan.el.addClass(dir); // or prev
20884             pan.el.addClass('carousel-item-' + dir); // or prev
20885             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20886             cur.el.addClass(lr); // or right
20887             pan.el.addClass(lr);
20888             cur.el.addClass('carousel-item-' +lr); // or right
20889             pan.el.addClass('carousel-item-' +lr);
20890             
20891             
20892             var _this = this;
20893             cur.el.on('transitionend', function() {
20894                 Roo.log("trans end?");
20895                 
20896                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20897                 pan.setActive(true);
20898                 
20899                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20900                 cur.setActive(false);
20901                 
20902                 _this.transition = false;
20903                 
20904             }, this, { single:  true } );
20905             
20906             return true;
20907         }
20908         
20909         cur.setActive(false);
20910         pan.setActive(true);
20911         
20912         return true;
20913         
20914     },
20915     showPanelNext : function()
20916     {
20917         var i = this.indexOfPanel(this.getActivePanel());
20918         
20919         if (i >= this.tabs.length - 1 && !this.autoslide) {
20920             return;
20921         }
20922         
20923         if (i >= this.tabs.length - 1 && this.autoslide) {
20924             i = -1;
20925         }
20926         
20927         this.showPanel(this.tabs[i+1]);
20928     },
20929     
20930     showPanelPrev : function()
20931     {
20932         var i = this.indexOfPanel(this.getActivePanel());
20933         
20934         if (i  < 1 && !this.autoslide) {
20935             return;
20936         }
20937         
20938         if (i < 1 && this.autoslide) {
20939             i = this.tabs.length;
20940         }
20941         
20942         this.showPanel(this.tabs[i-1]);
20943     },
20944     
20945     
20946     addBullet: function()
20947     {
20948         if(!this.bullets || Roo.isTouch){
20949             return;
20950         }
20951         var ctr = this.el.select('.carousel-bullets',true).first();
20952         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20953         var bullet = ctr.createChild({
20954             cls : 'bullet bullet-' + i
20955         },ctr.dom.lastChild);
20956         
20957         
20958         var _this = this;
20959         
20960         bullet.on('click', (function(e, el, o, ii, t){
20961
20962             e.preventDefault();
20963
20964             this.showPanel(ii);
20965
20966             if(this.autoslide && this.slideFn){
20967                 clearInterval(this.slideFn);
20968                 this.slideFn = window.setInterval(function() {
20969                     _this.showPanelNext();
20970                 }, this.timer);
20971             }
20972
20973         }).createDelegate(this, [i, bullet], true));
20974                 
20975         
20976     },
20977      
20978     setActiveBullet : function(i)
20979     {
20980         if(Roo.isTouch){
20981             return;
20982         }
20983         
20984         Roo.each(this.el.select('.bullet', true).elements, function(el){
20985             el.removeClass('selected');
20986         });
20987
20988         var bullet = this.el.select('.bullet-' + i, true).first();
20989         
20990         if(!bullet){
20991             return;
20992         }
20993         
20994         bullet.addClass('selected');
20995     }
20996     
20997     
20998   
20999 });
21000
21001  
21002
21003  
21004  
21005 Roo.apply(Roo.bootstrap.TabGroup, {
21006     
21007     groups: {},
21008      /**
21009     * register a Navigation Group
21010     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21011     */
21012     register : function(navgrp)
21013     {
21014         this.groups[navgrp.navId] = navgrp;
21015         
21016     },
21017     /**
21018     * fetch a Navigation Group based on the navigation ID
21019     * if one does not exist , it will get created.
21020     * @param {string} the navgroup to add
21021     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21022     */
21023     get: function(navId) {
21024         if (typeof(this.groups[navId]) == 'undefined') {
21025             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21026         }
21027         return this.groups[navId] ;
21028     }
21029     
21030     
21031     
21032 });
21033
21034  /*
21035  * - LGPL
21036  *
21037  * TabPanel
21038  * 
21039  */
21040
21041 /**
21042  * @class Roo.bootstrap.TabPanel
21043  * @extends Roo.bootstrap.Component
21044  * Bootstrap TabPanel class
21045  * @cfg {Boolean} active panel active
21046  * @cfg {String} html panel content
21047  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21048  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21049  * @cfg {String} href click to link..
21050  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21051  * 
21052  * 
21053  * @constructor
21054  * Create a new TabPanel
21055  * @param {Object} config The config object
21056  */
21057
21058 Roo.bootstrap.TabPanel = function(config){
21059     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21060     this.addEvents({
21061         /**
21062              * @event changed
21063              * Fires when the active status changes
21064              * @param {Roo.bootstrap.TabPanel} this
21065              * @param {Boolean} state the new state
21066             
21067          */
21068         'changed': true,
21069         /**
21070              * @event beforedeactivate
21071              * Fires before a tab is de-activated - can be used to do validation on a form.
21072              * @param {Roo.bootstrap.TabPanel} this
21073              * @return {Boolean} false if there is an error
21074             
21075          */
21076         'beforedeactivate': true
21077      });
21078     
21079     this.tabId = this.tabId || Roo.id();
21080   
21081 };
21082
21083 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21084     
21085     active: false,
21086     html: false,
21087     tabId: false,
21088     navId : false,
21089     href : '',
21090     touchSlide : false,
21091     getAutoCreate : function(){
21092         
21093         
21094         var cfg = {
21095             tag: 'div',
21096             // item is needed for carousel - not sure if it has any effect otherwise
21097             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21098             html: this.html || ''
21099         };
21100         
21101         if(this.active){
21102             cfg.cls += ' active';
21103         }
21104         
21105         if(this.tabId){
21106             cfg.tabId = this.tabId;
21107         }
21108         
21109         
21110         
21111         return cfg;
21112     },
21113     
21114     initEvents:  function()
21115     {
21116         var p = this.parent();
21117         
21118         this.navId = this.navId || p.navId;
21119         
21120         if (typeof(this.navId) != 'undefined') {
21121             // not really needed.. but just in case.. parent should be a NavGroup.
21122             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21123             
21124             tg.register(this);
21125             
21126             var i = tg.tabs.length - 1;
21127             
21128             if(this.active && tg.bullets > 0 && i < tg.bullets){
21129                 tg.setActiveBullet(i);
21130             }
21131         }
21132         
21133         this.el.on('click', this.onClick, this);
21134         
21135         if(Roo.isTouch && this.touchSlide){
21136             this.el.on("touchstart", this.onTouchStart, this);
21137             this.el.on("touchmove", this.onTouchMove, this);
21138             this.el.on("touchend", this.onTouchEnd, this);
21139         }
21140         
21141     },
21142     
21143     onRender : function(ct, position)
21144     {
21145         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21146     },
21147     
21148     setActive : function(state)
21149     {
21150         Roo.log("panel - set active " + this.tabId + "=" + state);
21151         
21152         this.active = state;
21153         if (!state) {
21154             this.el.removeClass('active');
21155             
21156         } else  if (!this.el.hasClass('active')) {
21157             this.el.addClass('active');
21158         }
21159         
21160         this.fireEvent('changed', this, state);
21161     },
21162     
21163     onClick : function(e)
21164     {
21165         e.preventDefault();
21166         
21167         if(!this.href.length){
21168             return;
21169         }
21170         
21171         window.location.href = this.href;
21172     },
21173     
21174     startX : 0,
21175     startY : 0,
21176     endX : 0,
21177     endY : 0,
21178     swiping : false,
21179     
21180     onTouchStart : function(e)
21181     {
21182         this.swiping = false;
21183         
21184         this.startX = e.browserEvent.touches[0].clientX;
21185         this.startY = e.browserEvent.touches[0].clientY;
21186     },
21187     
21188     onTouchMove : function(e)
21189     {
21190         this.swiping = true;
21191         
21192         this.endX = e.browserEvent.touches[0].clientX;
21193         this.endY = e.browserEvent.touches[0].clientY;
21194     },
21195     
21196     onTouchEnd : function(e)
21197     {
21198         if(!this.swiping){
21199             this.onClick(e);
21200             return;
21201         }
21202         
21203         var tabGroup = this.parent();
21204         
21205         if(this.endX > this.startX){ // swiping right
21206             tabGroup.showPanelPrev();
21207             return;
21208         }
21209         
21210         if(this.startX > this.endX){ // swiping left
21211             tabGroup.showPanelNext();
21212             return;
21213         }
21214     }
21215     
21216     
21217 });
21218  
21219
21220  
21221
21222  /*
21223  * - LGPL
21224  *
21225  * DateField
21226  * 
21227  */
21228
21229 /**
21230  * @class Roo.bootstrap.DateField
21231  * @extends Roo.bootstrap.Input
21232  * Bootstrap DateField class
21233  * @cfg {Number} weekStart default 0
21234  * @cfg {String} viewMode default empty, (months|years)
21235  * @cfg {String} minViewMode default empty, (months|years)
21236  * @cfg {Number} startDate default -Infinity
21237  * @cfg {Number} endDate default Infinity
21238  * @cfg {Boolean} todayHighlight default false
21239  * @cfg {Boolean} todayBtn default false
21240  * @cfg {Boolean} calendarWeeks default false
21241  * @cfg {Object} daysOfWeekDisabled default empty
21242  * @cfg {Boolean} singleMode default false (true | false)
21243  * 
21244  * @cfg {Boolean} keyboardNavigation default true
21245  * @cfg {String} language default en
21246  * 
21247  * @constructor
21248  * Create a new DateField
21249  * @param {Object} config The config object
21250  */
21251
21252 Roo.bootstrap.DateField = function(config){
21253     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21254      this.addEvents({
21255             /**
21256              * @event show
21257              * Fires when this field show.
21258              * @param {Roo.bootstrap.DateField} this
21259              * @param {Mixed} date The date value
21260              */
21261             show : true,
21262             /**
21263              * @event show
21264              * Fires when this field hide.
21265              * @param {Roo.bootstrap.DateField} this
21266              * @param {Mixed} date The date value
21267              */
21268             hide : true,
21269             /**
21270              * @event select
21271              * Fires when select a date.
21272              * @param {Roo.bootstrap.DateField} this
21273              * @param {Mixed} date The date value
21274              */
21275             select : true,
21276             /**
21277              * @event beforeselect
21278              * Fires when before select a date.
21279              * @param {Roo.bootstrap.DateField} this
21280              * @param {Mixed} date The date value
21281              */
21282             beforeselect : true
21283         });
21284 };
21285
21286 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21287     
21288     /**
21289      * @cfg {String} format
21290      * The default date format string which can be overriden for localization support.  The format must be
21291      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21292      */
21293     format : "m/d/y",
21294     /**
21295      * @cfg {String} altFormats
21296      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21297      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21298      */
21299     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21300     
21301     weekStart : 0,
21302     
21303     viewMode : '',
21304     
21305     minViewMode : '',
21306     
21307     todayHighlight : false,
21308     
21309     todayBtn: false,
21310     
21311     language: 'en',
21312     
21313     keyboardNavigation: true,
21314     
21315     calendarWeeks: false,
21316     
21317     startDate: -Infinity,
21318     
21319     endDate: Infinity,
21320     
21321     daysOfWeekDisabled: [],
21322     
21323     _events: [],
21324     
21325     singleMode : false,
21326     
21327     UTCDate: function()
21328     {
21329         return new Date(Date.UTC.apply(Date, arguments));
21330     },
21331     
21332     UTCToday: function()
21333     {
21334         var today = new Date();
21335         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21336     },
21337     
21338     getDate: function() {
21339             var d = this.getUTCDate();
21340             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21341     },
21342     
21343     getUTCDate: function() {
21344             return this.date;
21345     },
21346     
21347     setDate: function(d) {
21348             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21349     },
21350     
21351     setUTCDate: function(d) {
21352             this.date = d;
21353             this.setValue(this.formatDate(this.date));
21354     },
21355         
21356     onRender: function(ct, position)
21357     {
21358         
21359         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21360         
21361         this.language = this.language || 'en';
21362         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21363         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21364         
21365         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21366         this.format = this.format || 'm/d/y';
21367         this.isInline = false;
21368         this.isInput = true;
21369         this.component = this.el.select('.add-on', true).first() || false;
21370         this.component = (this.component && this.component.length === 0) ? false : this.component;
21371         this.hasInput = this.component && this.inputEl().length;
21372         
21373         if (typeof(this.minViewMode === 'string')) {
21374             switch (this.minViewMode) {
21375                 case 'months':
21376                     this.minViewMode = 1;
21377                     break;
21378                 case 'years':
21379                     this.minViewMode = 2;
21380                     break;
21381                 default:
21382                     this.minViewMode = 0;
21383                     break;
21384             }
21385         }
21386         
21387         if (typeof(this.viewMode === 'string')) {
21388             switch (this.viewMode) {
21389                 case 'months':
21390                     this.viewMode = 1;
21391                     break;
21392                 case 'years':
21393                     this.viewMode = 2;
21394                     break;
21395                 default:
21396                     this.viewMode = 0;
21397                     break;
21398             }
21399         }
21400                 
21401         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21402         
21403 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21404         
21405         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21406         
21407         this.picker().on('mousedown', this.onMousedown, this);
21408         this.picker().on('click', this.onClick, this);
21409         
21410         this.picker().addClass('datepicker-dropdown');
21411         
21412         this.startViewMode = this.viewMode;
21413         
21414         if(this.singleMode){
21415             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21416                 v.setVisibilityMode(Roo.Element.DISPLAY);
21417                 v.hide();
21418             });
21419             
21420             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21421                 v.setStyle('width', '189px');
21422             });
21423         }
21424         
21425         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21426             if(!this.calendarWeeks){
21427                 v.remove();
21428                 return;
21429             }
21430             
21431             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21432             v.attr('colspan', function(i, val){
21433                 return parseInt(val) + 1;
21434             });
21435         });
21436                         
21437         
21438         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21439         
21440         this.setStartDate(this.startDate);
21441         this.setEndDate(this.endDate);
21442         
21443         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21444         
21445         this.fillDow();
21446         this.fillMonths();
21447         this.update();
21448         this.showMode();
21449         
21450         if(this.isInline) {
21451             this.showPopup();
21452         }
21453     },
21454     
21455     picker : function()
21456     {
21457         return this.pickerEl;
21458 //        return this.el.select('.datepicker', true).first();
21459     },
21460     
21461     fillDow: function()
21462     {
21463         var dowCnt = this.weekStart;
21464         
21465         var dow = {
21466             tag: 'tr',
21467             cn: [
21468                 
21469             ]
21470         };
21471         
21472         if(this.calendarWeeks){
21473             dow.cn.push({
21474                 tag: 'th',
21475                 cls: 'cw',
21476                 html: '&nbsp;'
21477             })
21478         }
21479         
21480         while (dowCnt < this.weekStart + 7) {
21481             dow.cn.push({
21482                 tag: 'th',
21483                 cls: 'dow',
21484                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21485             });
21486         }
21487         
21488         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21489     },
21490     
21491     fillMonths: function()
21492     {    
21493         var i = 0;
21494         var months = this.picker().select('>.datepicker-months td', true).first();
21495         
21496         months.dom.innerHTML = '';
21497         
21498         while (i < 12) {
21499             var month = {
21500                 tag: 'span',
21501                 cls: 'month',
21502                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21503             };
21504             
21505             months.createChild(month);
21506         }
21507         
21508     },
21509     
21510     update: function()
21511     {
21512         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;
21513         
21514         if (this.date < this.startDate) {
21515             this.viewDate = new Date(this.startDate);
21516         } else if (this.date > this.endDate) {
21517             this.viewDate = new Date(this.endDate);
21518         } else {
21519             this.viewDate = new Date(this.date);
21520         }
21521         
21522         this.fill();
21523     },
21524     
21525     fill: function() 
21526     {
21527         var d = new Date(this.viewDate),
21528                 year = d.getUTCFullYear(),
21529                 month = d.getUTCMonth(),
21530                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21531                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21532                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21533                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21534                 currentDate = this.date && this.date.valueOf(),
21535                 today = this.UTCToday();
21536         
21537         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21538         
21539 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21540         
21541 //        this.picker.select('>tfoot th.today').
21542 //                                              .text(dates[this.language].today)
21543 //                                              .toggle(this.todayBtn !== false);
21544     
21545         this.updateNavArrows();
21546         this.fillMonths();
21547                                                 
21548         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21549         
21550         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21551          
21552         prevMonth.setUTCDate(day);
21553         
21554         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21555         
21556         var nextMonth = new Date(prevMonth);
21557         
21558         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21559         
21560         nextMonth = nextMonth.valueOf();
21561         
21562         var fillMonths = false;
21563         
21564         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21565         
21566         while(prevMonth.valueOf() <= nextMonth) {
21567             var clsName = '';
21568             
21569             if (prevMonth.getUTCDay() === this.weekStart) {
21570                 if(fillMonths){
21571                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21572                 }
21573                     
21574                 fillMonths = {
21575                     tag: 'tr',
21576                     cn: []
21577                 };
21578                 
21579                 if(this.calendarWeeks){
21580                     // ISO 8601: First week contains first thursday.
21581                     // ISO also states week starts on Monday, but we can be more abstract here.
21582                     var
21583                     // Start of current week: based on weekstart/current date
21584                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21585                     // Thursday of this week
21586                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21587                     // First Thursday of year, year from thursday
21588                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21589                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21590                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21591                     
21592                     fillMonths.cn.push({
21593                         tag: 'td',
21594                         cls: 'cw',
21595                         html: calWeek
21596                     });
21597                 }
21598             }
21599             
21600             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21601                 clsName += ' old';
21602             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21603                 clsName += ' new';
21604             }
21605             if (this.todayHighlight &&
21606                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21607                 prevMonth.getUTCMonth() == today.getMonth() &&
21608                 prevMonth.getUTCDate() == today.getDate()) {
21609                 clsName += ' today';
21610             }
21611             
21612             if (currentDate && prevMonth.valueOf() === currentDate) {
21613                 clsName += ' active';
21614             }
21615             
21616             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21617                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21618                     clsName += ' disabled';
21619             }
21620             
21621             fillMonths.cn.push({
21622                 tag: 'td',
21623                 cls: 'day ' + clsName,
21624                 html: prevMonth.getDate()
21625             });
21626             
21627             prevMonth.setDate(prevMonth.getDate()+1);
21628         }
21629           
21630         var currentYear = this.date && this.date.getUTCFullYear();
21631         var currentMonth = this.date && this.date.getUTCMonth();
21632         
21633         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21634         
21635         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21636             v.removeClass('active');
21637             
21638             if(currentYear === year && k === currentMonth){
21639                 v.addClass('active');
21640             }
21641             
21642             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21643                 v.addClass('disabled');
21644             }
21645             
21646         });
21647         
21648         
21649         year = parseInt(year/10, 10) * 10;
21650         
21651         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21652         
21653         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21654         
21655         year -= 1;
21656         for (var i = -1; i < 11; i++) {
21657             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21658                 tag: 'span',
21659                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21660                 html: year
21661             });
21662             
21663             year += 1;
21664         }
21665     },
21666     
21667     showMode: function(dir) 
21668     {
21669         if (dir) {
21670             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21671         }
21672         
21673         Roo.each(this.picker().select('>div',true).elements, function(v){
21674             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21675             v.hide();
21676         });
21677         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21678     },
21679     
21680     place: function()
21681     {
21682         if(this.isInline) {
21683             return;
21684         }
21685         
21686         this.picker().removeClass(['bottom', 'top']);
21687         
21688         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21689             /*
21690              * place to the top of element!
21691              *
21692              */
21693             
21694             this.picker().addClass('top');
21695             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21696             
21697             return;
21698         }
21699         
21700         this.picker().addClass('bottom');
21701         
21702         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21703     },
21704     
21705     parseDate : function(value)
21706     {
21707         if(!value || value instanceof Date){
21708             return value;
21709         }
21710         var v = Date.parseDate(value, this.format);
21711         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21712             v = Date.parseDate(value, 'Y-m-d');
21713         }
21714         if(!v && this.altFormats){
21715             if(!this.altFormatsArray){
21716                 this.altFormatsArray = this.altFormats.split("|");
21717             }
21718             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21719                 v = Date.parseDate(value, this.altFormatsArray[i]);
21720             }
21721         }
21722         return v;
21723     },
21724     
21725     formatDate : function(date, fmt)
21726     {   
21727         return (!date || !(date instanceof Date)) ?
21728         date : date.dateFormat(fmt || this.format);
21729     },
21730     
21731     onFocus : function()
21732     {
21733         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21734         this.showPopup();
21735     },
21736     
21737     onBlur : function()
21738     {
21739         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21740         
21741         var d = this.inputEl().getValue();
21742         
21743         this.setValue(d);
21744                 
21745         this.hidePopup();
21746     },
21747     
21748     showPopup : function()
21749     {
21750         this.picker().show();
21751         this.update();
21752         this.place();
21753         
21754         this.fireEvent('showpopup', this, this.date);
21755     },
21756     
21757     hidePopup : function()
21758     {
21759         if(this.isInline) {
21760             return;
21761         }
21762         this.picker().hide();
21763         this.viewMode = this.startViewMode;
21764         this.showMode();
21765         
21766         this.fireEvent('hidepopup', this, this.date);
21767         
21768     },
21769     
21770     onMousedown: function(e)
21771     {
21772         e.stopPropagation();
21773         e.preventDefault();
21774     },
21775     
21776     keyup: function(e)
21777     {
21778         Roo.bootstrap.DateField.superclass.keyup.call(this);
21779         this.update();
21780     },
21781
21782     setValue: function(v)
21783     {
21784         if(this.fireEvent('beforeselect', this, v) !== false){
21785             var d = new Date(this.parseDate(v) ).clearTime();
21786         
21787             if(isNaN(d.getTime())){
21788                 this.date = this.viewDate = '';
21789                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21790                 return;
21791             }
21792
21793             v = this.formatDate(d);
21794
21795             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21796
21797             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21798
21799             this.update();
21800
21801             this.fireEvent('select', this, this.date);
21802         }
21803     },
21804     
21805     getValue: function()
21806     {
21807         return this.formatDate(this.date);
21808     },
21809     
21810     fireKey: function(e)
21811     {
21812         if (!this.picker().isVisible()){
21813             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21814                 this.showPopup();
21815             }
21816             return;
21817         }
21818         
21819         var dateChanged = false,
21820         dir, day, month,
21821         newDate, newViewDate;
21822         
21823         switch(e.keyCode){
21824             case 27: // escape
21825                 this.hidePopup();
21826                 e.preventDefault();
21827                 break;
21828             case 37: // left
21829             case 39: // right
21830                 if (!this.keyboardNavigation) {
21831                     break;
21832                 }
21833                 dir = e.keyCode == 37 ? -1 : 1;
21834                 
21835                 if (e.ctrlKey){
21836                     newDate = this.moveYear(this.date, dir);
21837                     newViewDate = this.moveYear(this.viewDate, dir);
21838                 } else if (e.shiftKey){
21839                     newDate = this.moveMonth(this.date, dir);
21840                     newViewDate = this.moveMonth(this.viewDate, dir);
21841                 } else {
21842                     newDate = new Date(this.date);
21843                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21844                     newViewDate = new Date(this.viewDate);
21845                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21846                 }
21847                 if (this.dateWithinRange(newDate)){
21848                     this.date = newDate;
21849                     this.viewDate = newViewDate;
21850                     this.setValue(this.formatDate(this.date));
21851 //                    this.update();
21852                     e.preventDefault();
21853                     dateChanged = true;
21854                 }
21855                 break;
21856             case 38: // up
21857             case 40: // down
21858                 if (!this.keyboardNavigation) {
21859                     break;
21860                 }
21861                 dir = e.keyCode == 38 ? -1 : 1;
21862                 if (e.ctrlKey){
21863                     newDate = this.moveYear(this.date, dir);
21864                     newViewDate = this.moveYear(this.viewDate, dir);
21865                 } else if (e.shiftKey){
21866                     newDate = this.moveMonth(this.date, dir);
21867                     newViewDate = this.moveMonth(this.viewDate, dir);
21868                 } else {
21869                     newDate = new Date(this.date);
21870                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21871                     newViewDate = new Date(this.viewDate);
21872                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21873                 }
21874                 if (this.dateWithinRange(newDate)){
21875                     this.date = newDate;
21876                     this.viewDate = newViewDate;
21877                     this.setValue(this.formatDate(this.date));
21878 //                    this.update();
21879                     e.preventDefault();
21880                     dateChanged = true;
21881                 }
21882                 break;
21883             case 13: // enter
21884                 this.setValue(this.formatDate(this.date));
21885                 this.hidePopup();
21886                 e.preventDefault();
21887                 break;
21888             case 9: // tab
21889                 this.setValue(this.formatDate(this.date));
21890                 this.hidePopup();
21891                 break;
21892             case 16: // shift
21893             case 17: // ctrl
21894             case 18: // alt
21895                 break;
21896             default :
21897                 this.hidePopup();
21898                 
21899         }
21900     },
21901     
21902     
21903     onClick: function(e) 
21904     {
21905         e.stopPropagation();
21906         e.preventDefault();
21907         
21908         var target = e.getTarget();
21909         
21910         if(target.nodeName.toLowerCase() === 'i'){
21911             target = Roo.get(target).dom.parentNode;
21912         }
21913         
21914         var nodeName = target.nodeName;
21915         var className = target.className;
21916         var html = target.innerHTML;
21917         //Roo.log(nodeName);
21918         
21919         switch(nodeName.toLowerCase()) {
21920             case 'th':
21921                 switch(className) {
21922                     case 'switch':
21923                         this.showMode(1);
21924                         break;
21925                     case 'prev':
21926                     case 'next':
21927                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21928                         switch(this.viewMode){
21929                                 case 0:
21930                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21931                                         break;
21932                                 case 1:
21933                                 case 2:
21934                                         this.viewDate = this.moveYear(this.viewDate, dir);
21935                                         break;
21936                         }
21937                         this.fill();
21938                         break;
21939                     case 'today':
21940                         var date = new Date();
21941                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21942 //                        this.fill()
21943                         this.setValue(this.formatDate(this.date));
21944                         
21945                         this.hidePopup();
21946                         break;
21947                 }
21948                 break;
21949             case 'span':
21950                 if (className.indexOf('disabled') < 0) {
21951                     this.viewDate.setUTCDate(1);
21952                     if (className.indexOf('month') > -1) {
21953                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21954                     } else {
21955                         var year = parseInt(html, 10) || 0;
21956                         this.viewDate.setUTCFullYear(year);
21957                         
21958                     }
21959                     
21960                     if(this.singleMode){
21961                         this.setValue(this.formatDate(this.viewDate));
21962                         this.hidePopup();
21963                         return;
21964                     }
21965                     
21966                     this.showMode(-1);
21967                     this.fill();
21968                 }
21969                 break;
21970                 
21971             case 'td':
21972                 //Roo.log(className);
21973                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21974                     var day = parseInt(html, 10) || 1;
21975                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21976                         month = (this.viewDate || new Date()).getUTCMonth();
21977
21978                     if (className.indexOf('old') > -1) {
21979                         if(month === 0 ){
21980                             month = 11;
21981                             year -= 1;
21982                         }else{
21983                             month -= 1;
21984                         }
21985                     } else if (className.indexOf('new') > -1) {
21986                         if (month == 11) {
21987                             month = 0;
21988                             year += 1;
21989                         } else {
21990                             month += 1;
21991                         }
21992                     }
21993                     //Roo.log([year,month,day]);
21994                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21995                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21996 //                    this.fill();
21997                     //Roo.log(this.formatDate(this.date));
21998                     this.setValue(this.formatDate(this.date));
21999                     this.hidePopup();
22000                 }
22001                 break;
22002         }
22003     },
22004     
22005     setStartDate: function(startDate)
22006     {
22007         this.startDate = startDate || -Infinity;
22008         if (this.startDate !== -Infinity) {
22009             this.startDate = this.parseDate(this.startDate);
22010         }
22011         this.update();
22012         this.updateNavArrows();
22013     },
22014
22015     setEndDate: function(endDate)
22016     {
22017         this.endDate = endDate || Infinity;
22018         if (this.endDate !== Infinity) {
22019             this.endDate = this.parseDate(this.endDate);
22020         }
22021         this.update();
22022         this.updateNavArrows();
22023     },
22024     
22025     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22026     {
22027         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22028         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22029             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22030         }
22031         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22032             return parseInt(d, 10);
22033         });
22034         this.update();
22035         this.updateNavArrows();
22036     },
22037     
22038     updateNavArrows: function() 
22039     {
22040         if(this.singleMode){
22041             return;
22042         }
22043         
22044         var d = new Date(this.viewDate),
22045         year = d.getUTCFullYear(),
22046         month = d.getUTCMonth();
22047         
22048         Roo.each(this.picker().select('.prev', true).elements, function(v){
22049             v.show();
22050             switch (this.viewMode) {
22051                 case 0:
22052
22053                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22054                         v.hide();
22055                     }
22056                     break;
22057                 case 1:
22058                 case 2:
22059                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22060                         v.hide();
22061                     }
22062                     break;
22063             }
22064         });
22065         
22066         Roo.each(this.picker().select('.next', true).elements, function(v){
22067             v.show();
22068             switch (this.viewMode) {
22069                 case 0:
22070
22071                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22072                         v.hide();
22073                     }
22074                     break;
22075                 case 1:
22076                 case 2:
22077                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22078                         v.hide();
22079                     }
22080                     break;
22081             }
22082         })
22083     },
22084     
22085     moveMonth: function(date, dir)
22086     {
22087         if (!dir) {
22088             return date;
22089         }
22090         var new_date = new Date(date.valueOf()),
22091         day = new_date.getUTCDate(),
22092         month = new_date.getUTCMonth(),
22093         mag = Math.abs(dir),
22094         new_month, test;
22095         dir = dir > 0 ? 1 : -1;
22096         if (mag == 1){
22097             test = dir == -1
22098             // If going back one month, make sure month is not current month
22099             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22100             ? function(){
22101                 return new_date.getUTCMonth() == month;
22102             }
22103             // If going forward one month, make sure month is as expected
22104             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22105             : function(){
22106                 return new_date.getUTCMonth() != new_month;
22107             };
22108             new_month = month + dir;
22109             new_date.setUTCMonth(new_month);
22110             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22111             if (new_month < 0 || new_month > 11) {
22112                 new_month = (new_month + 12) % 12;
22113             }
22114         } else {
22115             // For magnitudes >1, move one month at a time...
22116             for (var i=0; i<mag; i++) {
22117                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22118                 new_date = this.moveMonth(new_date, dir);
22119             }
22120             // ...then reset the day, keeping it in the new month
22121             new_month = new_date.getUTCMonth();
22122             new_date.setUTCDate(day);
22123             test = function(){
22124                 return new_month != new_date.getUTCMonth();
22125             };
22126         }
22127         // Common date-resetting loop -- if date is beyond end of month, make it
22128         // end of month
22129         while (test()){
22130             new_date.setUTCDate(--day);
22131             new_date.setUTCMonth(new_month);
22132         }
22133         return new_date;
22134     },
22135
22136     moveYear: function(date, dir)
22137     {
22138         return this.moveMonth(date, dir*12);
22139     },
22140
22141     dateWithinRange: function(date)
22142     {
22143         return date >= this.startDate && date <= this.endDate;
22144     },
22145
22146     
22147     remove: function() 
22148     {
22149         this.picker().remove();
22150     },
22151     
22152     validateValue : function(value)
22153     {
22154         if(this.getVisibilityEl().hasClass('hidden')){
22155             return true;
22156         }
22157         
22158         if(value.length < 1)  {
22159             if(this.allowBlank){
22160                 return true;
22161             }
22162             return false;
22163         }
22164         
22165         if(value.length < this.minLength){
22166             return false;
22167         }
22168         if(value.length > this.maxLength){
22169             return false;
22170         }
22171         if(this.vtype){
22172             var vt = Roo.form.VTypes;
22173             if(!vt[this.vtype](value, this)){
22174                 return false;
22175             }
22176         }
22177         if(typeof this.validator == "function"){
22178             var msg = this.validator(value);
22179             if(msg !== true){
22180                 return false;
22181             }
22182         }
22183         
22184         if(this.regex && !this.regex.test(value)){
22185             return false;
22186         }
22187         
22188         if(typeof(this.parseDate(value)) == 'undefined'){
22189             return false;
22190         }
22191         
22192         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22193             return false;
22194         }      
22195         
22196         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22197             return false;
22198         } 
22199         
22200         
22201         return true;
22202     },
22203     
22204     reset : function()
22205     {
22206         this.date = this.viewDate = '';
22207         
22208         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22209     }
22210    
22211 });
22212
22213 Roo.apply(Roo.bootstrap.DateField,  {
22214     
22215     head : {
22216         tag: 'thead',
22217         cn: [
22218         {
22219             tag: 'tr',
22220             cn: [
22221             {
22222                 tag: 'th',
22223                 cls: 'prev',
22224                 html: '<i class="fa fa-arrow-left"/>'
22225             },
22226             {
22227                 tag: 'th',
22228                 cls: 'switch',
22229                 colspan: '5'
22230             },
22231             {
22232                 tag: 'th',
22233                 cls: 'next',
22234                 html: '<i class="fa fa-arrow-right"/>'
22235             }
22236
22237             ]
22238         }
22239         ]
22240     },
22241     
22242     content : {
22243         tag: 'tbody',
22244         cn: [
22245         {
22246             tag: 'tr',
22247             cn: [
22248             {
22249                 tag: 'td',
22250                 colspan: '7'
22251             }
22252             ]
22253         }
22254         ]
22255     },
22256     
22257     footer : {
22258         tag: 'tfoot',
22259         cn: [
22260         {
22261             tag: 'tr',
22262             cn: [
22263             {
22264                 tag: 'th',
22265                 colspan: '7',
22266                 cls: 'today'
22267             }
22268                     
22269             ]
22270         }
22271         ]
22272     },
22273     
22274     dates:{
22275         en: {
22276             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22277             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22278             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22279             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22280             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22281             today: "Today"
22282         }
22283     },
22284     
22285     modes: [
22286     {
22287         clsName: 'days',
22288         navFnc: 'Month',
22289         navStep: 1
22290     },
22291     {
22292         clsName: 'months',
22293         navFnc: 'FullYear',
22294         navStep: 1
22295     },
22296     {
22297         clsName: 'years',
22298         navFnc: 'FullYear',
22299         navStep: 10
22300     }]
22301 });
22302
22303 Roo.apply(Roo.bootstrap.DateField,  {
22304   
22305     template : {
22306         tag: 'div',
22307         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22308         cn: [
22309         {
22310             tag: 'div',
22311             cls: 'datepicker-days',
22312             cn: [
22313             {
22314                 tag: 'table',
22315                 cls: 'table-condensed',
22316                 cn:[
22317                 Roo.bootstrap.DateField.head,
22318                 {
22319                     tag: 'tbody'
22320                 },
22321                 Roo.bootstrap.DateField.footer
22322                 ]
22323             }
22324             ]
22325         },
22326         {
22327             tag: 'div',
22328             cls: 'datepicker-months',
22329             cn: [
22330             {
22331                 tag: 'table',
22332                 cls: 'table-condensed',
22333                 cn:[
22334                 Roo.bootstrap.DateField.head,
22335                 Roo.bootstrap.DateField.content,
22336                 Roo.bootstrap.DateField.footer
22337                 ]
22338             }
22339             ]
22340         },
22341         {
22342             tag: 'div',
22343             cls: 'datepicker-years',
22344             cn: [
22345             {
22346                 tag: 'table',
22347                 cls: 'table-condensed',
22348                 cn:[
22349                 Roo.bootstrap.DateField.head,
22350                 Roo.bootstrap.DateField.content,
22351                 Roo.bootstrap.DateField.footer
22352                 ]
22353             }
22354             ]
22355         }
22356         ]
22357     }
22358 });
22359
22360  
22361
22362  /*
22363  * - LGPL
22364  *
22365  * TimeField
22366  * 
22367  */
22368
22369 /**
22370  * @class Roo.bootstrap.TimeField
22371  * @extends Roo.bootstrap.Input
22372  * Bootstrap DateField class
22373  * 
22374  * 
22375  * @constructor
22376  * Create a new TimeField
22377  * @param {Object} config The config object
22378  */
22379
22380 Roo.bootstrap.TimeField = function(config){
22381     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22382     this.addEvents({
22383             /**
22384              * @event show
22385              * Fires when this field show.
22386              * @param {Roo.bootstrap.DateField} thisthis
22387              * @param {Mixed} date The date value
22388              */
22389             show : true,
22390             /**
22391              * @event show
22392              * Fires when this field hide.
22393              * @param {Roo.bootstrap.DateField} this
22394              * @param {Mixed} date The date value
22395              */
22396             hide : true,
22397             /**
22398              * @event select
22399              * Fires when select a date.
22400              * @param {Roo.bootstrap.DateField} this
22401              * @param {Mixed} date The date value
22402              */
22403             select : true
22404         });
22405 };
22406
22407 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22408     
22409     /**
22410      * @cfg {String} format
22411      * The default time format string which can be overriden for localization support.  The format must be
22412      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22413      */
22414     format : "H:i",
22415
22416     getAutoCreate : function()
22417     {
22418         this.after = '<i class="fa far fa-clock"></i>';
22419         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22420         
22421          
22422     },
22423     onRender: function(ct, position)
22424     {
22425         
22426         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22427                 
22428         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22429         
22430         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22431         
22432         this.pop = this.picker().select('>.datepicker-time',true).first();
22433         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22434         
22435         this.picker().on('mousedown', this.onMousedown, this);
22436         this.picker().on('click', this.onClick, this);
22437         
22438         this.picker().addClass('datepicker-dropdown');
22439     
22440         this.fillTime();
22441         this.update();
22442             
22443         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22444         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22445         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22446         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22447         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22448         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22449
22450     },
22451     
22452     fireKey: function(e){
22453         if (!this.picker().isVisible()){
22454             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22455                 this.show();
22456             }
22457             return;
22458         }
22459
22460         e.preventDefault();
22461         
22462         switch(e.keyCode){
22463             case 27: // escape
22464                 this.hide();
22465                 break;
22466             case 37: // left
22467             case 39: // right
22468                 this.onTogglePeriod();
22469                 break;
22470             case 38: // up
22471                 this.onIncrementMinutes();
22472                 break;
22473             case 40: // down
22474                 this.onDecrementMinutes();
22475                 break;
22476             case 13: // enter
22477             case 9: // tab
22478                 this.setTime();
22479                 break;
22480         }
22481     },
22482     
22483     onClick: function(e) {
22484         e.stopPropagation();
22485         e.preventDefault();
22486     },
22487     
22488     picker : function()
22489     {
22490         return this.pickerEl;
22491     },
22492     
22493     fillTime: function()
22494     {    
22495         var time = this.pop.select('tbody', true).first();
22496         
22497         time.dom.innerHTML = '';
22498         
22499         time.createChild({
22500             tag: 'tr',
22501             cn: [
22502                 {
22503                     tag: 'td',
22504                     cn: [
22505                         {
22506                             tag: 'a',
22507                             href: '#',
22508                             cls: 'btn',
22509                             cn: [
22510                                 {
22511                                     tag: 'i',
22512                                     cls: 'hours-up fa fas fa-chevron-up'
22513                                 }
22514                             ]
22515                         } 
22516                     ]
22517                 },
22518                 {
22519                     tag: 'td',
22520                     cls: 'separator'
22521                 },
22522                 {
22523                     tag: 'td',
22524                     cn: [
22525                         {
22526                             tag: 'a',
22527                             href: '#',
22528                             cls: 'btn',
22529                             cn: [
22530                                 {
22531                                     tag: 'i',
22532                                     cls: 'minutes-up fa fas fa-chevron-up'
22533                                 }
22534                             ]
22535                         }
22536                     ]
22537                 },
22538                 {
22539                     tag: 'td',
22540                     cls: 'separator'
22541                 }
22542             ]
22543         });
22544         
22545         time.createChild({
22546             tag: 'tr',
22547             cn: [
22548                 {
22549                     tag: 'td',
22550                     cn: [
22551                         {
22552                             tag: 'span',
22553                             cls: 'timepicker-hour',
22554                             html: '00'
22555                         }  
22556                     ]
22557                 },
22558                 {
22559                     tag: 'td',
22560                     cls: 'separator',
22561                     html: ':'
22562                 },
22563                 {
22564                     tag: 'td',
22565                     cn: [
22566                         {
22567                             tag: 'span',
22568                             cls: 'timepicker-minute',
22569                             html: '00'
22570                         }  
22571                     ]
22572                 },
22573                 {
22574                     tag: 'td',
22575                     cls: 'separator'
22576                 },
22577                 {
22578                     tag: 'td',
22579                     cn: [
22580                         {
22581                             tag: 'button',
22582                             type: 'button',
22583                             cls: 'btn btn-primary period',
22584                             html: 'AM'
22585                             
22586                         }
22587                     ]
22588                 }
22589             ]
22590         });
22591         
22592         time.createChild({
22593             tag: 'tr',
22594             cn: [
22595                 {
22596                     tag: 'td',
22597                     cn: [
22598                         {
22599                             tag: 'a',
22600                             href: '#',
22601                             cls: 'btn',
22602                             cn: [
22603                                 {
22604                                     tag: 'span',
22605                                     cls: 'hours-down fa fas fa-chevron-down'
22606                                 }
22607                             ]
22608                         }
22609                     ]
22610                 },
22611                 {
22612                     tag: 'td',
22613                     cls: 'separator'
22614                 },
22615                 {
22616                     tag: 'td',
22617                     cn: [
22618                         {
22619                             tag: 'a',
22620                             href: '#',
22621                             cls: 'btn',
22622                             cn: [
22623                                 {
22624                                     tag: 'span',
22625                                     cls: 'minutes-down fa fas fa-chevron-down'
22626                                 }
22627                             ]
22628                         }
22629                     ]
22630                 },
22631                 {
22632                     tag: 'td',
22633                     cls: 'separator'
22634                 }
22635             ]
22636         });
22637         
22638     },
22639     
22640     update: function()
22641     {
22642         
22643         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22644         
22645         this.fill();
22646     },
22647     
22648     fill: function() 
22649     {
22650         var hours = this.time.getHours();
22651         var minutes = this.time.getMinutes();
22652         var period = 'AM';
22653         
22654         if(hours > 11){
22655             period = 'PM';
22656         }
22657         
22658         if(hours == 0){
22659             hours = 12;
22660         }
22661         
22662         
22663         if(hours > 12){
22664             hours = hours - 12;
22665         }
22666         
22667         if(hours < 10){
22668             hours = '0' + hours;
22669         }
22670         
22671         if(minutes < 10){
22672             minutes = '0' + minutes;
22673         }
22674         
22675         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22676         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22677         this.pop.select('button', true).first().dom.innerHTML = period;
22678         
22679     },
22680     
22681     place: function()
22682     {   
22683         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22684         
22685         var cls = ['bottom'];
22686         
22687         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22688             cls.pop();
22689             cls.push('top');
22690         }
22691         
22692         cls.push('right');
22693         
22694         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22695             cls.pop();
22696             cls.push('left');
22697         }
22698         //this.picker().setXY(20000,20000);
22699         this.picker().addClass(cls.join('-'));
22700         
22701         var _this = this;
22702         
22703         Roo.each(cls, function(c){
22704             if(c == 'bottom'){
22705                 (function() {
22706                  //  
22707                 }).defer(200);
22708                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22709                 //_this.picker().setTop(_this.inputEl().getHeight());
22710                 return;
22711             }
22712             if(c == 'top'){
22713                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22714                 
22715                 //_this.picker().setTop(0 - _this.picker().getHeight());
22716                 return;
22717             }
22718             /*
22719             if(c == 'left'){
22720                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22721                 return;
22722             }
22723             if(c == 'right'){
22724                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22725                 return;
22726             }
22727             */
22728         });
22729         
22730     },
22731   
22732     onFocus : function()
22733     {
22734         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22735         this.show();
22736     },
22737     
22738     onBlur : function()
22739     {
22740         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22741         this.hide();
22742     },
22743     
22744     show : function()
22745     {
22746         this.picker().show();
22747         this.pop.show();
22748         this.update();
22749         this.place();
22750         
22751         this.fireEvent('show', this, this.date);
22752     },
22753     
22754     hide : function()
22755     {
22756         this.picker().hide();
22757         this.pop.hide();
22758         
22759         this.fireEvent('hide', this, this.date);
22760     },
22761     
22762     setTime : function()
22763     {
22764         this.hide();
22765         this.setValue(this.time.format(this.format));
22766         
22767         this.fireEvent('select', this, this.date);
22768         
22769         
22770     },
22771     
22772     onMousedown: function(e){
22773         e.stopPropagation();
22774         e.preventDefault();
22775     },
22776     
22777     onIncrementHours: function()
22778     {
22779         Roo.log('onIncrementHours');
22780         this.time = this.time.add(Date.HOUR, 1);
22781         this.update();
22782         
22783     },
22784     
22785     onDecrementHours: function()
22786     {
22787         Roo.log('onDecrementHours');
22788         this.time = this.time.add(Date.HOUR, -1);
22789         this.update();
22790     },
22791     
22792     onIncrementMinutes: function()
22793     {
22794         Roo.log('onIncrementMinutes');
22795         this.time = this.time.add(Date.MINUTE, 1);
22796         this.update();
22797     },
22798     
22799     onDecrementMinutes: function()
22800     {
22801         Roo.log('onDecrementMinutes');
22802         this.time = this.time.add(Date.MINUTE, -1);
22803         this.update();
22804     },
22805     
22806     onTogglePeriod: function()
22807     {
22808         Roo.log('onTogglePeriod');
22809         this.time = this.time.add(Date.HOUR, 12);
22810         this.update();
22811     }
22812     
22813    
22814 });
22815  
22816
22817 Roo.apply(Roo.bootstrap.TimeField,  {
22818   
22819     template : {
22820         tag: 'div',
22821         cls: 'datepicker dropdown-menu',
22822         cn: [
22823             {
22824                 tag: 'div',
22825                 cls: 'datepicker-time',
22826                 cn: [
22827                 {
22828                     tag: 'table',
22829                     cls: 'table-condensed',
22830                     cn:[
22831                         {
22832                             tag: 'tbody',
22833                             cn: [
22834                                 {
22835                                     tag: 'tr',
22836                                     cn: [
22837                                     {
22838                                         tag: 'td',
22839                                         colspan: '7'
22840                                     }
22841                                     ]
22842                                 }
22843                             ]
22844                         },
22845                         {
22846                             tag: 'tfoot',
22847                             cn: [
22848                                 {
22849                                     tag: 'tr',
22850                                     cn: [
22851                                     {
22852                                         tag: 'th',
22853                                         colspan: '7',
22854                                         cls: '',
22855                                         cn: [
22856                                             {
22857                                                 tag: 'button',
22858                                                 cls: 'btn btn-info ok',
22859                                                 html: 'OK'
22860                                             }
22861                                         ]
22862                                     }
22863                     
22864                                     ]
22865                                 }
22866                             ]
22867                         }
22868                     ]
22869                 }
22870                 ]
22871             }
22872         ]
22873     }
22874 });
22875
22876  
22877
22878  /*
22879  * - LGPL
22880  *
22881  * MonthField
22882  * 
22883  */
22884
22885 /**
22886  * @class Roo.bootstrap.MonthField
22887  * @extends Roo.bootstrap.Input
22888  * Bootstrap MonthField class
22889  * 
22890  * @cfg {String} language default en
22891  * 
22892  * @constructor
22893  * Create a new MonthField
22894  * @param {Object} config The config object
22895  */
22896
22897 Roo.bootstrap.MonthField = function(config){
22898     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22899     
22900     this.addEvents({
22901         /**
22902          * @event show
22903          * Fires when this field show.
22904          * @param {Roo.bootstrap.MonthField} this
22905          * @param {Mixed} date The date value
22906          */
22907         show : true,
22908         /**
22909          * @event show
22910          * Fires when this field hide.
22911          * @param {Roo.bootstrap.MonthField} this
22912          * @param {Mixed} date The date value
22913          */
22914         hide : true,
22915         /**
22916          * @event select
22917          * Fires when select a date.
22918          * @param {Roo.bootstrap.MonthField} this
22919          * @param {String} oldvalue The old value
22920          * @param {String} newvalue The new value
22921          */
22922         select : true
22923     });
22924 };
22925
22926 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22927     
22928     onRender: function(ct, position)
22929     {
22930         
22931         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22932         
22933         this.language = this.language || 'en';
22934         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22935         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22936         
22937         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22938         this.isInline = false;
22939         this.isInput = true;
22940         this.component = this.el.select('.add-on', true).first() || false;
22941         this.component = (this.component && this.component.length === 0) ? false : this.component;
22942         this.hasInput = this.component && this.inputEL().length;
22943         
22944         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22945         
22946         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22947         
22948         this.picker().on('mousedown', this.onMousedown, this);
22949         this.picker().on('click', this.onClick, this);
22950         
22951         this.picker().addClass('datepicker-dropdown');
22952         
22953         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22954             v.setStyle('width', '189px');
22955         });
22956         
22957         this.fillMonths();
22958         
22959         this.update();
22960         
22961         if(this.isInline) {
22962             this.show();
22963         }
22964         
22965     },
22966     
22967     setValue: function(v, suppressEvent)
22968     {   
22969         var o = this.getValue();
22970         
22971         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22972         
22973         this.update();
22974
22975         if(suppressEvent !== true){
22976             this.fireEvent('select', this, o, v);
22977         }
22978         
22979     },
22980     
22981     getValue: function()
22982     {
22983         return this.value;
22984     },
22985     
22986     onClick: function(e) 
22987     {
22988         e.stopPropagation();
22989         e.preventDefault();
22990         
22991         var target = e.getTarget();
22992         
22993         if(target.nodeName.toLowerCase() === 'i'){
22994             target = Roo.get(target).dom.parentNode;
22995         }
22996         
22997         var nodeName = target.nodeName;
22998         var className = target.className;
22999         var html = target.innerHTML;
23000         
23001         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23002             return;
23003         }
23004         
23005         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23006         
23007         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23008         
23009         this.hide();
23010                         
23011     },
23012     
23013     picker : function()
23014     {
23015         return this.pickerEl;
23016     },
23017     
23018     fillMonths: function()
23019     {    
23020         var i = 0;
23021         var months = this.picker().select('>.datepicker-months td', true).first();
23022         
23023         months.dom.innerHTML = '';
23024         
23025         while (i < 12) {
23026             var month = {
23027                 tag: 'span',
23028                 cls: 'month',
23029                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23030             };
23031             
23032             months.createChild(month);
23033         }
23034         
23035     },
23036     
23037     update: function()
23038     {
23039         var _this = this;
23040         
23041         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23042             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23043         }
23044         
23045         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23046             e.removeClass('active');
23047             
23048             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23049                 e.addClass('active');
23050             }
23051         })
23052     },
23053     
23054     place: function()
23055     {
23056         if(this.isInline) {
23057             return;
23058         }
23059         
23060         this.picker().removeClass(['bottom', 'top']);
23061         
23062         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23063             /*
23064              * place to the top of element!
23065              *
23066              */
23067             
23068             this.picker().addClass('top');
23069             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23070             
23071             return;
23072         }
23073         
23074         this.picker().addClass('bottom');
23075         
23076         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23077     },
23078     
23079     onFocus : function()
23080     {
23081         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23082         this.show();
23083     },
23084     
23085     onBlur : function()
23086     {
23087         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23088         
23089         var d = this.inputEl().getValue();
23090         
23091         this.setValue(d);
23092                 
23093         this.hide();
23094     },
23095     
23096     show : function()
23097     {
23098         this.picker().show();
23099         this.picker().select('>.datepicker-months', true).first().show();
23100         this.update();
23101         this.place();
23102         
23103         this.fireEvent('show', this, this.date);
23104     },
23105     
23106     hide : function()
23107     {
23108         if(this.isInline) {
23109             return;
23110         }
23111         this.picker().hide();
23112         this.fireEvent('hide', this, this.date);
23113         
23114     },
23115     
23116     onMousedown: function(e)
23117     {
23118         e.stopPropagation();
23119         e.preventDefault();
23120     },
23121     
23122     keyup: function(e)
23123     {
23124         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23125         this.update();
23126     },
23127
23128     fireKey: function(e)
23129     {
23130         if (!this.picker().isVisible()){
23131             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23132                 this.show();
23133             }
23134             return;
23135         }
23136         
23137         var dir;
23138         
23139         switch(e.keyCode){
23140             case 27: // escape
23141                 this.hide();
23142                 e.preventDefault();
23143                 break;
23144             case 37: // left
23145             case 39: // right
23146                 dir = e.keyCode == 37 ? -1 : 1;
23147                 
23148                 this.vIndex = this.vIndex + dir;
23149                 
23150                 if(this.vIndex < 0){
23151                     this.vIndex = 0;
23152                 }
23153                 
23154                 if(this.vIndex > 11){
23155                     this.vIndex = 11;
23156                 }
23157                 
23158                 if(isNaN(this.vIndex)){
23159                     this.vIndex = 0;
23160                 }
23161                 
23162                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23163                 
23164                 break;
23165             case 38: // up
23166             case 40: // down
23167                 
23168                 dir = e.keyCode == 38 ? -1 : 1;
23169                 
23170                 this.vIndex = this.vIndex + dir * 4;
23171                 
23172                 if(this.vIndex < 0){
23173                     this.vIndex = 0;
23174                 }
23175                 
23176                 if(this.vIndex > 11){
23177                     this.vIndex = 11;
23178                 }
23179                 
23180                 if(isNaN(this.vIndex)){
23181                     this.vIndex = 0;
23182                 }
23183                 
23184                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23185                 break;
23186                 
23187             case 13: // enter
23188                 
23189                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23190                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23191                 }
23192                 
23193                 this.hide();
23194                 e.preventDefault();
23195                 break;
23196             case 9: // tab
23197                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23198                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23199                 }
23200                 this.hide();
23201                 break;
23202             case 16: // shift
23203             case 17: // ctrl
23204             case 18: // alt
23205                 break;
23206             default :
23207                 this.hide();
23208                 
23209         }
23210     },
23211     
23212     remove: function() 
23213     {
23214         this.picker().remove();
23215     }
23216    
23217 });
23218
23219 Roo.apply(Roo.bootstrap.MonthField,  {
23220     
23221     content : {
23222         tag: 'tbody',
23223         cn: [
23224         {
23225             tag: 'tr',
23226             cn: [
23227             {
23228                 tag: 'td',
23229                 colspan: '7'
23230             }
23231             ]
23232         }
23233         ]
23234     },
23235     
23236     dates:{
23237         en: {
23238             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23239             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23240         }
23241     }
23242 });
23243
23244 Roo.apply(Roo.bootstrap.MonthField,  {
23245   
23246     template : {
23247         tag: 'div',
23248         cls: 'datepicker dropdown-menu roo-dynamic',
23249         cn: [
23250             {
23251                 tag: 'div',
23252                 cls: 'datepicker-months',
23253                 cn: [
23254                 {
23255                     tag: 'table',
23256                     cls: 'table-condensed',
23257                     cn:[
23258                         Roo.bootstrap.DateField.content
23259                     ]
23260                 }
23261                 ]
23262             }
23263         ]
23264     }
23265 });
23266
23267  
23268
23269  
23270  /*
23271  * - LGPL
23272  *
23273  * CheckBox
23274  * 
23275  */
23276
23277 /**
23278  * @class Roo.bootstrap.CheckBox
23279  * @extends Roo.bootstrap.Input
23280  * Bootstrap CheckBox class
23281  * 
23282  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23283  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23284  * @cfg {String} boxLabel The text that appears beside the checkbox
23285  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23286  * @cfg {Boolean} checked initnal the element
23287  * @cfg {Boolean} inline inline the element (default false)
23288  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23289  * @cfg {String} tooltip label tooltip
23290  * 
23291  * @constructor
23292  * Create a new CheckBox
23293  * @param {Object} config The config object
23294  */
23295
23296 Roo.bootstrap.CheckBox = function(config){
23297     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23298    
23299     this.addEvents({
23300         /**
23301         * @event check
23302         * Fires when the element is checked or unchecked.
23303         * @param {Roo.bootstrap.CheckBox} this This input
23304         * @param {Boolean} checked The new checked value
23305         */
23306        check : true,
23307        /**
23308         * @event click
23309         * Fires when the element is click.
23310         * @param {Roo.bootstrap.CheckBox} this This input
23311         */
23312        click : true
23313     });
23314     
23315 };
23316
23317 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23318   
23319     inputType: 'checkbox',
23320     inputValue: 1,
23321     valueOff: 0,
23322     boxLabel: false,
23323     checked: false,
23324     weight : false,
23325     inline: false,
23326     tooltip : '',
23327     
23328     // checkbox success does not make any sense really.. 
23329     invalidClass : "",
23330     validClass : "",
23331     
23332     
23333     getAutoCreate : function()
23334     {
23335         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23336         
23337         var id = Roo.id();
23338         
23339         var cfg = {};
23340         
23341         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23342         
23343         if(this.inline){
23344             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23345         }
23346         
23347         var input =  {
23348             tag: 'input',
23349             id : id,
23350             type : this.inputType,
23351             value : this.inputValue,
23352             cls : 'roo-' + this.inputType, //'form-box',
23353             placeholder : this.placeholder || ''
23354             
23355         };
23356         
23357         if(this.inputType != 'radio'){
23358             var hidden =  {
23359                 tag: 'input',
23360                 type : 'hidden',
23361                 cls : 'roo-hidden-value',
23362                 value : this.checked ? this.inputValue : this.valueOff
23363             };
23364         }
23365         
23366             
23367         if (this.weight) { // Validity check?
23368             cfg.cls += " " + this.inputType + "-" + this.weight;
23369         }
23370         
23371         if (this.disabled) {
23372             input.disabled=true;
23373         }
23374         
23375         if(this.checked){
23376             input.checked = this.checked;
23377         }
23378         
23379         if (this.name) {
23380             
23381             input.name = this.name;
23382             
23383             if(this.inputType != 'radio'){
23384                 hidden.name = this.name;
23385                 input.name = '_hidden_' + this.name;
23386             }
23387         }
23388         
23389         if (this.size) {
23390             input.cls += ' input-' + this.size;
23391         }
23392         
23393         var settings=this;
23394         
23395         ['xs','sm','md','lg'].map(function(size){
23396             if (settings[size]) {
23397                 cfg.cls += ' col-' + size + '-' + settings[size];
23398             }
23399         });
23400         
23401         var inputblock = input;
23402          
23403         if (this.before || this.after) {
23404             
23405             inputblock = {
23406                 cls : 'input-group',
23407                 cn :  [] 
23408             };
23409             
23410             if (this.before) {
23411                 inputblock.cn.push({
23412                     tag :'span',
23413                     cls : 'input-group-addon',
23414                     html : this.before
23415                 });
23416             }
23417             
23418             inputblock.cn.push(input);
23419             
23420             if(this.inputType != 'radio'){
23421                 inputblock.cn.push(hidden);
23422             }
23423             
23424             if (this.after) {
23425                 inputblock.cn.push({
23426                     tag :'span',
23427                     cls : 'input-group-addon',
23428                     html : this.after
23429                 });
23430             }
23431             
23432         }
23433         var boxLabelCfg = false;
23434         
23435         if(this.boxLabel){
23436            
23437             boxLabelCfg = {
23438                 tag: 'label',
23439                 //'for': id, // box label is handled by onclick - so no for...
23440                 cls: 'box-label',
23441                 html: this.boxLabel
23442             };
23443             if(this.tooltip){
23444                 boxLabelCfg.tooltip = this.tooltip;
23445             }
23446              
23447         }
23448         
23449         
23450         if (align ==='left' && this.fieldLabel.length) {
23451 //                Roo.log("left and has label");
23452             cfg.cn = [
23453                 {
23454                     tag: 'label',
23455                     'for' :  id,
23456                     cls : 'control-label',
23457                     html : this.fieldLabel
23458                 },
23459                 {
23460                     cls : "", 
23461                     cn: [
23462                         inputblock
23463                     ]
23464                 }
23465             ];
23466             
23467             if (boxLabelCfg) {
23468                 cfg.cn[1].cn.push(boxLabelCfg);
23469             }
23470             
23471             if(this.labelWidth > 12){
23472                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23473             }
23474             
23475             if(this.labelWidth < 13 && this.labelmd == 0){
23476                 this.labelmd = this.labelWidth;
23477             }
23478             
23479             if(this.labellg > 0){
23480                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23481                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23482             }
23483             
23484             if(this.labelmd > 0){
23485                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23486                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23487             }
23488             
23489             if(this.labelsm > 0){
23490                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23491                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23492             }
23493             
23494             if(this.labelxs > 0){
23495                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23496                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23497             }
23498             
23499         } else if ( this.fieldLabel.length) {
23500 //                Roo.log(" label");
23501                 cfg.cn = [
23502                    
23503                     {
23504                         tag: this.boxLabel ? 'span' : 'label',
23505                         'for': id,
23506                         cls: 'control-label box-input-label',
23507                         //cls : 'input-group-addon',
23508                         html : this.fieldLabel
23509                     },
23510                     
23511                     inputblock
23512                     
23513                 ];
23514                 if (boxLabelCfg) {
23515                     cfg.cn.push(boxLabelCfg);
23516                 }
23517
23518         } else {
23519             
23520 //                Roo.log(" no label && no align");
23521                 cfg.cn = [  inputblock ] ;
23522                 if (boxLabelCfg) {
23523                     cfg.cn.push(boxLabelCfg);
23524                 }
23525
23526                 
23527         }
23528         
23529        
23530         
23531         if(this.inputType != 'radio'){
23532             cfg.cn.push(hidden);
23533         }
23534         
23535         return cfg;
23536         
23537     },
23538     
23539     /**
23540      * return the real input element.
23541      */
23542     inputEl: function ()
23543     {
23544         return this.el.select('input.roo-' + this.inputType,true).first();
23545     },
23546     hiddenEl: function ()
23547     {
23548         return this.el.select('input.roo-hidden-value',true).first();
23549     },
23550     
23551     labelEl: function()
23552     {
23553         return this.el.select('label.control-label',true).first();
23554     },
23555     /* depricated... */
23556     
23557     label: function()
23558     {
23559         return this.labelEl();
23560     },
23561     
23562     boxLabelEl: function()
23563     {
23564         return this.el.select('label.box-label',true).first();
23565     },
23566     
23567     initEvents : function()
23568     {
23569 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23570         
23571         this.inputEl().on('click', this.onClick,  this);
23572         
23573         if (this.boxLabel) { 
23574             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23575         }
23576         
23577         this.startValue = this.getValue();
23578         
23579         if(this.groupId){
23580             Roo.bootstrap.CheckBox.register(this);
23581         }
23582     },
23583     
23584     onClick : function(e)
23585     {   
23586         if(this.fireEvent('click', this, e) !== false){
23587             this.setChecked(!this.checked);
23588         }
23589         
23590     },
23591     
23592     setChecked : function(state,suppressEvent)
23593     {
23594         this.startValue = this.getValue();
23595
23596         if(this.inputType == 'radio'){
23597             
23598             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23599                 e.dom.checked = false;
23600             });
23601             
23602             this.inputEl().dom.checked = true;
23603             
23604             this.inputEl().dom.value = this.inputValue;
23605             
23606             if(suppressEvent !== true){
23607                 this.fireEvent('check', this, true);
23608             }
23609             
23610             this.validate();
23611             
23612             return;
23613         }
23614         
23615         this.checked = state;
23616         
23617         this.inputEl().dom.checked = state;
23618         
23619         
23620         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23621         
23622         if(suppressEvent !== true){
23623             this.fireEvent('check', this, state);
23624         }
23625         
23626         this.validate();
23627     },
23628     
23629     getValue : function()
23630     {
23631         if(this.inputType == 'radio'){
23632             return this.getGroupValue();
23633         }
23634         
23635         return this.hiddenEl().dom.value;
23636         
23637     },
23638     
23639     getGroupValue : function()
23640     {
23641         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23642             return '';
23643         }
23644         
23645         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23646     },
23647     
23648     setValue : function(v,suppressEvent)
23649     {
23650         if(this.inputType == 'radio'){
23651             this.setGroupValue(v, suppressEvent);
23652             return;
23653         }
23654         
23655         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23656         
23657         this.validate();
23658     },
23659     
23660     setGroupValue : function(v, suppressEvent)
23661     {
23662         this.startValue = this.getValue();
23663         
23664         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23665             e.dom.checked = false;
23666             
23667             if(e.dom.value == v){
23668                 e.dom.checked = true;
23669             }
23670         });
23671         
23672         if(suppressEvent !== true){
23673             this.fireEvent('check', this, true);
23674         }
23675
23676         this.validate();
23677         
23678         return;
23679     },
23680     
23681     validate : function()
23682     {
23683         if(this.getVisibilityEl().hasClass('hidden')){
23684             return true;
23685         }
23686         
23687         if(
23688                 this.disabled || 
23689                 (this.inputType == 'radio' && this.validateRadio()) ||
23690                 (this.inputType == 'checkbox' && this.validateCheckbox())
23691         ){
23692             this.markValid();
23693             return true;
23694         }
23695         
23696         this.markInvalid();
23697         return false;
23698     },
23699     
23700     validateRadio : function()
23701     {
23702         if(this.getVisibilityEl().hasClass('hidden')){
23703             return true;
23704         }
23705         
23706         if(this.allowBlank){
23707             return true;
23708         }
23709         
23710         var valid = false;
23711         
23712         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23713             if(!e.dom.checked){
23714                 return;
23715             }
23716             
23717             valid = true;
23718             
23719             return false;
23720         });
23721         
23722         return valid;
23723     },
23724     
23725     validateCheckbox : function()
23726     {
23727         if(!this.groupId){
23728             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23729             //return (this.getValue() == this.inputValue) ? true : false;
23730         }
23731         
23732         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23733         
23734         if(!group){
23735             return false;
23736         }
23737         
23738         var r = false;
23739         
23740         for(var i in group){
23741             if(group[i].el.isVisible(true)){
23742                 r = false;
23743                 break;
23744             }
23745             
23746             r = true;
23747         }
23748         
23749         for(var i in group){
23750             if(r){
23751                 break;
23752             }
23753             
23754             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23755         }
23756         
23757         return r;
23758     },
23759     
23760     /**
23761      * Mark this field as valid
23762      */
23763     markValid : function()
23764     {
23765         var _this = this;
23766         
23767         this.fireEvent('valid', this);
23768         
23769         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23770         
23771         if(this.groupId){
23772             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23773         }
23774         
23775         if(label){
23776             label.markValid();
23777         }
23778
23779         if(this.inputType == 'radio'){
23780             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23781                 var fg = e.findParent('.form-group', false, true);
23782                 if (Roo.bootstrap.version == 3) {
23783                     fg.removeClass([_this.invalidClass, _this.validClass]);
23784                     fg.addClass(_this.validClass);
23785                 } else {
23786                     fg.removeClass(['is-valid', 'is-invalid']);
23787                     fg.addClass('is-valid');
23788                 }
23789             });
23790             
23791             return;
23792         }
23793
23794         if(!this.groupId){
23795             var fg = this.el.findParent('.form-group', false, true);
23796             if (Roo.bootstrap.version == 3) {
23797                 fg.removeClass([this.invalidClass, this.validClass]);
23798                 fg.addClass(this.validClass);
23799             } else {
23800                 fg.removeClass(['is-valid', 'is-invalid']);
23801                 fg.addClass('is-valid');
23802             }
23803             return;
23804         }
23805         
23806         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23807         
23808         if(!group){
23809             return;
23810         }
23811         
23812         for(var i in group){
23813             var fg = group[i].el.findParent('.form-group', false, true);
23814             if (Roo.bootstrap.version == 3) {
23815                 fg.removeClass([this.invalidClass, this.validClass]);
23816                 fg.addClass(this.validClass);
23817             } else {
23818                 fg.removeClass(['is-valid', 'is-invalid']);
23819                 fg.addClass('is-valid');
23820             }
23821         }
23822     },
23823     
23824      /**
23825      * Mark this field as invalid
23826      * @param {String} msg The validation message
23827      */
23828     markInvalid : function(msg)
23829     {
23830         if(this.allowBlank){
23831             return;
23832         }
23833         
23834         var _this = this;
23835         
23836         this.fireEvent('invalid', this, msg);
23837         
23838         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23839         
23840         if(this.groupId){
23841             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23842         }
23843         
23844         if(label){
23845             label.markInvalid();
23846         }
23847             
23848         if(this.inputType == 'radio'){
23849             
23850             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23851                 var fg = e.findParent('.form-group', false, true);
23852                 if (Roo.bootstrap.version == 3) {
23853                     fg.removeClass([_this.invalidClass, _this.validClass]);
23854                     fg.addClass(_this.invalidClass);
23855                 } else {
23856                     fg.removeClass(['is-invalid', 'is-valid']);
23857                     fg.addClass('is-invalid');
23858                 }
23859             });
23860             
23861             return;
23862         }
23863         
23864         if(!this.groupId){
23865             var fg = this.el.findParent('.form-group', false, true);
23866             if (Roo.bootstrap.version == 3) {
23867                 fg.removeClass([_this.invalidClass, _this.validClass]);
23868                 fg.addClass(_this.invalidClass);
23869             } else {
23870                 fg.removeClass(['is-invalid', 'is-valid']);
23871                 fg.addClass('is-invalid');
23872             }
23873             return;
23874         }
23875         
23876         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23877         
23878         if(!group){
23879             return;
23880         }
23881         
23882         for(var i in group){
23883             var fg = group[i].el.findParent('.form-group', false, true);
23884             if (Roo.bootstrap.version == 3) {
23885                 fg.removeClass([_this.invalidClass, _this.validClass]);
23886                 fg.addClass(_this.invalidClass);
23887             } else {
23888                 fg.removeClass(['is-invalid', 'is-valid']);
23889                 fg.addClass('is-invalid');
23890             }
23891         }
23892         
23893     },
23894     
23895     clearInvalid : function()
23896     {
23897         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23898         
23899         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23900         
23901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23902         
23903         if (label && label.iconEl) {
23904             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23905             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23906         }
23907     },
23908     
23909     disable : function()
23910     {
23911         if(this.inputType != 'radio'){
23912             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23913             return;
23914         }
23915         
23916         var _this = this;
23917         
23918         if(this.rendered){
23919             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23920                 _this.getActionEl().addClass(this.disabledClass);
23921                 e.dom.disabled = true;
23922             });
23923         }
23924         
23925         this.disabled = true;
23926         this.fireEvent("disable", this);
23927         return this;
23928     },
23929
23930     enable : function()
23931     {
23932         if(this.inputType != 'radio'){
23933             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23934             return;
23935         }
23936         
23937         var _this = this;
23938         
23939         if(this.rendered){
23940             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23941                 _this.getActionEl().removeClass(this.disabledClass);
23942                 e.dom.disabled = false;
23943             });
23944         }
23945         
23946         this.disabled = false;
23947         this.fireEvent("enable", this);
23948         return this;
23949     },
23950     
23951     setBoxLabel : function(v)
23952     {
23953         this.boxLabel = v;
23954         
23955         if(this.rendered){
23956             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23957         }
23958     }
23959
23960 });
23961
23962 Roo.apply(Roo.bootstrap.CheckBox, {
23963     
23964     groups: {},
23965     
23966      /**
23967     * register a CheckBox Group
23968     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23969     */
23970     register : function(checkbox)
23971     {
23972         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23973             this.groups[checkbox.groupId] = {};
23974         }
23975         
23976         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23977             return;
23978         }
23979         
23980         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23981         
23982     },
23983     /**
23984     * fetch a CheckBox Group based on the group ID
23985     * @param {string} the group ID
23986     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23987     */
23988     get: function(groupId) {
23989         if (typeof(this.groups[groupId]) == 'undefined') {
23990             return false;
23991         }
23992         
23993         return this.groups[groupId] ;
23994     }
23995     
23996     
23997 });
23998 /*
23999  * - LGPL
24000  *
24001  * RadioItem
24002  * 
24003  */
24004
24005 /**
24006  * @class Roo.bootstrap.Radio
24007  * @extends Roo.bootstrap.Component
24008  * Bootstrap Radio class
24009  * @cfg {String} boxLabel - the label associated
24010  * @cfg {String} value - the value of radio
24011  * 
24012  * @constructor
24013  * Create a new Radio
24014  * @param {Object} config The config object
24015  */
24016 Roo.bootstrap.Radio = function(config){
24017     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24018     
24019 };
24020
24021 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24022     
24023     boxLabel : '',
24024     
24025     value : '',
24026     
24027     getAutoCreate : function()
24028     {
24029         var cfg = {
24030             tag : 'div',
24031             cls : 'form-group radio',
24032             cn : [
24033                 {
24034                     tag : 'label',
24035                     cls : 'box-label',
24036                     html : this.boxLabel
24037                 }
24038             ]
24039         };
24040         
24041         return cfg;
24042     },
24043     
24044     initEvents : function() 
24045     {
24046         this.parent().register(this);
24047         
24048         this.el.on('click', this.onClick, this);
24049         
24050     },
24051     
24052     onClick : function(e)
24053     {
24054         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24055             this.setChecked(true);
24056         }
24057     },
24058     
24059     setChecked : function(state, suppressEvent)
24060     {
24061         this.parent().setValue(this.value, suppressEvent);
24062         
24063     },
24064     
24065     setBoxLabel : function(v)
24066     {
24067         this.boxLabel = v;
24068         
24069         if(this.rendered){
24070             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24071         }
24072     }
24073     
24074 });
24075  
24076
24077  /*
24078  * - LGPL
24079  *
24080  * Input
24081  * 
24082  */
24083
24084 /**
24085  * @class Roo.bootstrap.SecurePass
24086  * @extends Roo.bootstrap.Input
24087  * Bootstrap SecurePass class
24088  *
24089  * 
24090  * @constructor
24091  * Create a new SecurePass
24092  * @param {Object} config The config object
24093  */
24094  
24095 Roo.bootstrap.SecurePass = function (config) {
24096     // these go here, so the translation tool can replace them..
24097     this.errors = {
24098         PwdEmpty: "Please type a password, and then retype it to confirm.",
24099         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24100         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24101         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24102         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24103         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24104         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24105         TooWeak: "Your password is Too Weak."
24106     },
24107     this.meterLabel = "Password strength:";
24108     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24109     this.meterClass = [
24110         "roo-password-meter-tooweak", 
24111         "roo-password-meter-weak", 
24112         "roo-password-meter-medium", 
24113         "roo-password-meter-strong", 
24114         "roo-password-meter-grey"
24115     ];
24116     
24117     this.errors = {};
24118     
24119     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24120 }
24121
24122 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24123     /**
24124      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24125      * {
24126      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24127      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24128      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24129      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24130      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24131      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24132      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24133      * })
24134      */
24135     // private
24136     
24137     meterWidth: 300,
24138     errorMsg :'',    
24139     errors: false,
24140     imageRoot: '/',
24141     /**
24142      * @cfg {String/Object} Label for the strength meter (defaults to
24143      * 'Password strength:')
24144      */
24145     // private
24146     meterLabel: '',
24147     /**
24148      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24149      * ['Weak', 'Medium', 'Strong'])
24150      */
24151     // private    
24152     pwdStrengths: false,    
24153     // private
24154     strength: 0,
24155     // private
24156     _lastPwd: null,
24157     // private
24158     kCapitalLetter: 0,
24159     kSmallLetter: 1,
24160     kDigit: 2,
24161     kPunctuation: 3,
24162     
24163     insecure: false,
24164     // private
24165     initEvents: function ()
24166     {
24167         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24168
24169         if (this.el.is('input[type=password]') && Roo.isSafari) {
24170             this.el.on('keydown', this.SafariOnKeyDown, this);
24171         }
24172
24173         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24174     },
24175     // private
24176     onRender: function (ct, position)
24177     {
24178         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24179         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24180         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24181
24182         this.trigger.createChild({
24183                    cn: [
24184                     {
24185                     //id: 'PwdMeter',
24186                     tag: 'div',
24187                     cls: 'roo-password-meter-grey col-xs-12',
24188                     style: {
24189                         //width: 0,
24190                         //width: this.meterWidth + 'px'                                                
24191                         }
24192                     },
24193                     {                            
24194                          cls: 'roo-password-meter-text'                          
24195                     }
24196                 ]            
24197         });
24198
24199          
24200         if (this.hideTrigger) {
24201             this.trigger.setDisplayed(false);
24202         }
24203         this.setSize(this.width || '', this.height || '');
24204     },
24205     // private
24206     onDestroy: function ()
24207     {
24208         if (this.trigger) {
24209             this.trigger.removeAllListeners();
24210             this.trigger.remove();
24211         }
24212         if (this.wrap) {
24213             this.wrap.remove();
24214         }
24215         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24216     },
24217     // private
24218     checkStrength: function ()
24219     {
24220         var pwd = this.inputEl().getValue();
24221         if (pwd == this._lastPwd) {
24222             return;
24223         }
24224
24225         var strength;
24226         if (this.ClientSideStrongPassword(pwd)) {
24227             strength = 3;
24228         } else if (this.ClientSideMediumPassword(pwd)) {
24229             strength = 2;
24230         } else if (this.ClientSideWeakPassword(pwd)) {
24231             strength = 1;
24232         } else {
24233             strength = 0;
24234         }
24235         
24236         Roo.log('strength1: ' + strength);
24237         
24238         //var pm = this.trigger.child('div/div/div').dom;
24239         var pm = this.trigger.child('div/div');
24240         pm.removeClass(this.meterClass);
24241         pm.addClass(this.meterClass[strength]);
24242                 
24243         
24244         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24245                 
24246         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24247         
24248         this._lastPwd = pwd;
24249     },
24250     reset: function ()
24251     {
24252         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24253         
24254         this._lastPwd = '';
24255         
24256         var pm = this.trigger.child('div/div');
24257         pm.removeClass(this.meterClass);
24258         pm.addClass('roo-password-meter-grey');        
24259         
24260         
24261         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24262         
24263         pt.innerHTML = '';
24264         this.inputEl().dom.type='password';
24265     },
24266     // private
24267     validateValue: function (value)
24268     {
24269         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24270             return false;
24271         }
24272         if (value.length == 0) {
24273             if (this.allowBlank) {
24274                 this.clearInvalid();
24275                 return true;
24276             }
24277
24278             this.markInvalid(this.errors.PwdEmpty);
24279             this.errorMsg = this.errors.PwdEmpty;
24280             return false;
24281         }
24282         
24283         if(this.insecure){
24284             return true;
24285         }
24286         
24287         if (!value.match(/[\x21-\x7e]+/)) {
24288             this.markInvalid(this.errors.PwdBadChar);
24289             this.errorMsg = this.errors.PwdBadChar;
24290             return false;
24291         }
24292         if (value.length < 6) {
24293             this.markInvalid(this.errors.PwdShort);
24294             this.errorMsg = this.errors.PwdShort;
24295             return false;
24296         }
24297         if (value.length > 16) {
24298             this.markInvalid(this.errors.PwdLong);
24299             this.errorMsg = this.errors.PwdLong;
24300             return false;
24301         }
24302         var strength;
24303         if (this.ClientSideStrongPassword(value)) {
24304             strength = 3;
24305         } else if (this.ClientSideMediumPassword(value)) {
24306             strength = 2;
24307         } else if (this.ClientSideWeakPassword(value)) {
24308             strength = 1;
24309         } else {
24310             strength = 0;
24311         }
24312
24313         
24314         if (strength < 2) {
24315             //this.markInvalid(this.errors.TooWeak);
24316             this.errorMsg = this.errors.TooWeak;
24317             //return false;
24318         }
24319         
24320         
24321         console.log('strength2: ' + strength);
24322         
24323         //var pm = this.trigger.child('div/div/div').dom;
24324         
24325         var pm = this.trigger.child('div/div');
24326         pm.removeClass(this.meterClass);
24327         pm.addClass(this.meterClass[strength]);
24328                 
24329         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24330                 
24331         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24332         
24333         this.errorMsg = ''; 
24334         return true;
24335     },
24336     // private
24337     CharacterSetChecks: function (type)
24338     {
24339         this.type = type;
24340         this.fResult = false;
24341     },
24342     // private
24343     isctype: function (character, type)
24344     {
24345         switch (type) {  
24346             case this.kCapitalLetter:
24347                 if (character >= 'A' && character <= 'Z') {
24348                     return true;
24349                 }
24350                 break;
24351             
24352             case this.kSmallLetter:
24353                 if (character >= 'a' && character <= 'z') {
24354                     return true;
24355                 }
24356                 break;
24357             
24358             case this.kDigit:
24359                 if (character >= '0' && character <= '9') {
24360                     return true;
24361                 }
24362                 break;
24363             
24364             case this.kPunctuation:
24365                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24366                     return true;
24367                 }
24368                 break;
24369             
24370             default:
24371                 return false;
24372         }
24373
24374     },
24375     // private
24376     IsLongEnough: function (pwd, size)
24377     {
24378         return !(pwd == null || isNaN(size) || pwd.length < size);
24379     },
24380     // private
24381     SpansEnoughCharacterSets: function (word, nb)
24382     {
24383         if (!this.IsLongEnough(word, nb))
24384         {
24385             return false;
24386         }
24387
24388         var characterSetChecks = new Array(
24389             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24390             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24391         );
24392         
24393         for (var index = 0; index < word.length; ++index) {
24394             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24395                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24396                     characterSetChecks[nCharSet].fResult = true;
24397                     break;
24398                 }
24399             }
24400         }
24401
24402         var nCharSets = 0;
24403         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24404             if (characterSetChecks[nCharSet].fResult) {
24405                 ++nCharSets;
24406             }
24407         }
24408
24409         if (nCharSets < nb) {
24410             return false;
24411         }
24412         return true;
24413     },
24414     // private
24415     ClientSideStrongPassword: function (pwd)
24416     {
24417         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24418     },
24419     // private
24420     ClientSideMediumPassword: function (pwd)
24421     {
24422         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24423     },
24424     // private
24425     ClientSideWeakPassword: function (pwd)
24426     {
24427         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24428     }
24429           
24430 })//<script type="text/javascript">
24431
24432 /*
24433  * Based  Ext JS Library 1.1.1
24434  * Copyright(c) 2006-2007, Ext JS, LLC.
24435  * LGPL
24436  *
24437  */
24438  
24439 /**
24440  * @class Roo.HtmlEditorCore
24441  * @extends Roo.Component
24442  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24443  *
24444  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24445  */
24446
24447 Roo.HtmlEditorCore = function(config){
24448     
24449     
24450     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24451     
24452     
24453     this.addEvents({
24454         /**
24455          * @event initialize
24456          * Fires when the editor is fully initialized (including the iframe)
24457          * @param {Roo.HtmlEditorCore} this
24458          */
24459         initialize: true,
24460         /**
24461          * @event activate
24462          * Fires when the editor is first receives the focus. Any insertion must wait
24463          * until after this event.
24464          * @param {Roo.HtmlEditorCore} this
24465          */
24466         activate: true,
24467          /**
24468          * @event beforesync
24469          * Fires before the textarea is updated with content from the editor iframe. Return false
24470          * to cancel the sync.
24471          * @param {Roo.HtmlEditorCore} this
24472          * @param {String} html
24473          */
24474         beforesync: true,
24475          /**
24476          * @event beforepush
24477          * Fires before the iframe editor is updated with content from the textarea. Return false
24478          * to cancel the push.
24479          * @param {Roo.HtmlEditorCore} this
24480          * @param {String} html
24481          */
24482         beforepush: true,
24483          /**
24484          * @event sync
24485          * Fires when the textarea is updated with content from the editor iframe.
24486          * @param {Roo.HtmlEditorCore} this
24487          * @param {String} html
24488          */
24489         sync: true,
24490          /**
24491          * @event push
24492          * Fires when the iframe editor is updated with content from the textarea.
24493          * @param {Roo.HtmlEditorCore} this
24494          * @param {String} html
24495          */
24496         push: true,
24497         
24498         /**
24499          * @event editorevent
24500          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24501          * @param {Roo.HtmlEditorCore} this
24502          */
24503         editorevent: true
24504         
24505     });
24506     
24507     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24508     
24509     // defaults : white / black...
24510     this.applyBlacklists();
24511     
24512     
24513     
24514 };
24515
24516
24517 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24518
24519
24520      /**
24521      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24522      */
24523     
24524     owner : false,
24525     
24526      /**
24527      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24528      *                        Roo.resizable.
24529      */
24530     resizable : false,
24531      /**
24532      * @cfg {Number} height (in pixels)
24533      */   
24534     height: 300,
24535    /**
24536      * @cfg {Number} width (in pixels)
24537      */   
24538     width: 500,
24539     
24540     /**
24541      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24542      * 
24543      */
24544     stylesheets: false,
24545     
24546     // id of frame..
24547     frameId: false,
24548     
24549     // private properties
24550     validationEvent : false,
24551     deferHeight: true,
24552     initialized : false,
24553     activated : false,
24554     sourceEditMode : false,
24555     onFocus : Roo.emptyFn,
24556     iframePad:3,
24557     hideMode:'offsets',
24558     
24559     clearUp: true,
24560     
24561     // blacklist + whitelisted elements..
24562     black: false,
24563     white: false,
24564      
24565     bodyCls : '',
24566
24567     /**
24568      * Protected method that will not generally be called directly. It
24569      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24570      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24571      */
24572     getDocMarkup : function(){
24573         // body styles..
24574         var st = '';
24575         
24576         // inherit styels from page...?? 
24577         if (this.stylesheets === false) {
24578             
24579             Roo.get(document.head).select('style').each(function(node) {
24580                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24581             });
24582             
24583             Roo.get(document.head).select('link').each(function(node) { 
24584                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24585             });
24586             
24587         } else if (!this.stylesheets.length) {
24588                 // simple..
24589                 st = '<style type="text/css">' +
24590                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24591                    '</style>';
24592         } else {
24593             for (var i in this.stylesheets) { 
24594                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24595             }
24596             
24597         }
24598         
24599         st +=  '<style type="text/css">' +
24600             'IMG { cursor: pointer } ' +
24601         '</style>';
24602
24603         var cls = 'roo-htmleditor-body';
24604         
24605         if(this.bodyCls.length){
24606             cls += ' ' + this.bodyCls;
24607         }
24608         
24609         return '<html><head>' + st  +
24610             //<style type="text/css">' +
24611             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24612             //'</style>' +
24613             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24614     },
24615
24616     // private
24617     onRender : function(ct, position)
24618     {
24619         var _t = this;
24620         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24621         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24622         
24623         
24624         this.el.dom.style.border = '0 none';
24625         this.el.dom.setAttribute('tabIndex', -1);
24626         this.el.addClass('x-hidden hide');
24627         
24628         
24629         
24630         if(Roo.isIE){ // fix IE 1px bogus margin
24631             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24632         }
24633        
24634         
24635         this.frameId = Roo.id();
24636         
24637          
24638         
24639         var iframe = this.owner.wrap.createChild({
24640             tag: 'iframe',
24641             cls: 'form-control', // bootstrap..
24642             id: this.frameId,
24643             name: this.frameId,
24644             frameBorder : 'no',
24645             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24646         }, this.el
24647         );
24648         
24649         
24650         this.iframe = iframe.dom;
24651
24652          this.assignDocWin();
24653         
24654         this.doc.designMode = 'on';
24655        
24656         this.doc.open();
24657         this.doc.write(this.getDocMarkup());
24658         this.doc.close();
24659
24660         
24661         var task = { // must defer to wait for browser to be ready
24662             run : function(){
24663                 //console.log("run task?" + this.doc.readyState);
24664                 this.assignDocWin();
24665                 if(this.doc.body || this.doc.readyState == 'complete'){
24666                     try {
24667                         this.doc.designMode="on";
24668                     } catch (e) {
24669                         return;
24670                     }
24671                     Roo.TaskMgr.stop(task);
24672                     this.initEditor.defer(10, this);
24673                 }
24674             },
24675             interval : 10,
24676             duration: 10000,
24677             scope: this
24678         };
24679         Roo.TaskMgr.start(task);
24680
24681     },
24682
24683     // private
24684     onResize : function(w, h)
24685     {
24686          Roo.log('resize: ' +w + ',' + h );
24687         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24688         if(!this.iframe){
24689             return;
24690         }
24691         if(typeof w == 'number'){
24692             
24693             this.iframe.style.width = w + 'px';
24694         }
24695         if(typeof h == 'number'){
24696             
24697             this.iframe.style.height = h + 'px';
24698             if(this.doc){
24699                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24700             }
24701         }
24702         
24703     },
24704
24705     /**
24706      * Toggles the editor between standard and source edit mode.
24707      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24708      */
24709     toggleSourceEdit : function(sourceEditMode){
24710         
24711         this.sourceEditMode = sourceEditMode === true;
24712         
24713         if(this.sourceEditMode){
24714  
24715             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24716             
24717         }else{
24718             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24719             //this.iframe.className = '';
24720             this.deferFocus();
24721         }
24722         //this.setSize(this.owner.wrap.getSize());
24723         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24724     },
24725
24726     
24727   
24728
24729     /**
24730      * Protected method that will not generally be called directly. If you need/want
24731      * custom HTML cleanup, this is the method you should override.
24732      * @param {String} html The HTML to be cleaned
24733      * return {String} The cleaned HTML
24734      */
24735     cleanHtml : function(html){
24736         html = String(html);
24737         if(html.length > 5){
24738             if(Roo.isSafari){ // strip safari nonsense
24739                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24740             }
24741         }
24742         if(html == '&nbsp;'){
24743             html = '';
24744         }
24745         return html;
24746     },
24747
24748     /**
24749      * HTML Editor -> Textarea
24750      * Protected method that will not generally be called directly. Syncs the contents
24751      * of the editor iframe with the textarea.
24752      */
24753     syncValue : function(){
24754         if(this.initialized){
24755             var bd = (this.doc.body || this.doc.documentElement);
24756             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24757             var html = bd.innerHTML;
24758             if(Roo.isSafari){
24759                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24760                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24761                 if(m && m[1]){
24762                     html = '<div style="'+m[0]+'">' + html + '</div>';
24763                 }
24764             }
24765             html = this.cleanHtml(html);
24766             // fix up the special chars.. normaly like back quotes in word...
24767             // however we do not want to do this with chinese..
24768             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24769                 
24770                 var cc = match.charCodeAt();
24771
24772                 // Get the character value, handling surrogate pairs
24773                 if (match.length == 2) {
24774                     // It's a surrogate pair, calculate the Unicode code point
24775                     var high = match.charCodeAt(0) - 0xD800;
24776                     var low  = match.charCodeAt(1) - 0xDC00;
24777                     cc = (high * 0x400) + low + 0x10000;
24778                 }  else if (
24779                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24780                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24781                     (cc >= 0xf900 && cc < 0xfb00 )
24782                 ) {
24783                         return match;
24784                 }  
24785          
24786                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24787                 return "&#" + cc + ";";
24788                 
24789                 
24790             });
24791             
24792             
24793              
24794             if(this.owner.fireEvent('beforesync', this, html) !== false){
24795                 this.el.dom.value = html;
24796                 this.owner.fireEvent('sync', this, html);
24797             }
24798         }
24799     },
24800
24801     /**
24802      * Protected method that will not generally be called directly. Pushes the value of the textarea
24803      * into the iframe editor.
24804      */
24805     pushValue : function(){
24806         if(this.initialized){
24807             var v = this.el.dom.value.trim();
24808             
24809 //            if(v.length < 1){
24810 //                v = '&#160;';
24811 //            }
24812             
24813             if(this.owner.fireEvent('beforepush', this, v) !== false){
24814                 var d = (this.doc.body || this.doc.documentElement);
24815                 d.innerHTML = v;
24816                 this.cleanUpPaste();
24817                 this.el.dom.value = d.innerHTML;
24818                 this.owner.fireEvent('push', this, v);
24819             }
24820         }
24821     },
24822
24823     // private
24824     deferFocus : function(){
24825         this.focus.defer(10, this);
24826     },
24827
24828     // doc'ed in Field
24829     focus : function(){
24830         if(this.win && !this.sourceEditMode){
24831             this.win.focus();
24832         }else{
24833             this.el.focus();
24834         }
24835     },
24836     
24837     assignDocWin: function()
24838     {
24839         var iframe = this.iframe;
24840         
24841          if(Roo.isIE){
24842             this.doc = iframe.contentWindow.document;
24843             this.win = iframe.contentWindow;
24844         } else {
24845 //            if (!Roo.get(this.frameId)) {
24846 //                return;
24847 //            }
24848 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24849 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24850             
24851             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24852                 return;
24853             }
24854             
24855             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24856             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24857         }
24858     },
24859     
24860     // private
24861     initEditor : function(){
24862         //console.log("INIT EDITOR");
24863         this.assignDocWin();
24864         
24865         
24866         
24867         this.doc.designMode="on";
24868         this.doc.open();
24869         this.doc.write(this.getDocMarkup());
24870         this.doc.close();
24871         
24872         var dbody = (this.doc.body || this.doc.documentElement);
24873         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24874         // this copies styles from the containing element into thsi one..
24875         // not sure why we need all of this..
24876         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24877         
24878         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24879         //ss['background-attachment'] = 'fixed'; // w3c
24880         dbody.bgProperties = 'fixed'; // ie
24881         //Roo.DomHelper.applyStyles(dbody, ss);
24882         Roo.EventManager.on(this.doc, {
24883             //'mousedown': this.onEditorEvent,
24884             'mouseup': this.onEditorEvent,
24885             'dblclick': this.onEditorEvent,
24886             'click': this.onEditorEvent,
24887             'keyup': this.onEditorEvent,
24888             buffer:100,
24889             scope: this
24890         });
24891         if(Roo.isGecko){
24892             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24893         }
24894         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24895             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24896         }
24897         this.initialized = true;
24898
24899         this.owner.fireEvent('initialize', this);
24900         this.pushValue();
24901     },
24902
24903     // private
24904     onDestroy : function(){
24905         
24906         
24907         
24908         if(this.rendered){
24909             
24910             //for (var i =0; i < this.toolbars.length;i++) {
24911             //    // fixme - ask toolbars for heights?
24912             //    this.toolbars[i].onDestroy();
24913            // }
24914             
24915             //this.wrap.dom.innerHTML = '';
24916             //this.wrap.remove();
24917         }
24918     },
24919
24920     // private
24921     onFirstFocus : function(){
24922         
24923         this.assignDocWin();
24924         
24925         
24926         this.activated = true;
24927          
24928     
24929         if(Roo.isGecko){ // prevent silly gecko errors
24930             this.win.focus();
24931             var s = this.win.getSelection();
24932             if(!s.focusNode || s.focusNode.nodeType != 3){
24933                 var r = s.getRangeAt(0);
24934                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24935                 r.collapse(true);
24936                 this.deferFocus();
24937             }
24938             try{
24939                 this.execCmd('useCSS', true);
24940                 this.execCmd('styleWithCSS', false);
24941             }catch(e){}
24942         }
24943         this.owner.fireEvent('activate', this);
24944     },
24945
24946     // private
24947     adjustFont: function(btn){
24948         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24949         //if(Roo.isSafari){ // safari
24950         //    adjust *= 2;
24951        // }
24952         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24953         if(Roo.isSafari){ // safari
24954             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24955             v =  (v < 10) ? 10 : v;
24956             v =  (v > 48) ? 48 : v;
24957             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24958             
24959         }
24960         
24961         
24962         v = Math.max(1, v+adjust);
24963         
24964         this.execCmd('FontSize', v  );
24965     },
24966
24967     onEditorEvent : function(e)
24968     {
24969         this.owner.fireEvent('editorevent', this, e);
24970       //  this.updateToolbar();
24971         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24972     },
24973
24974     insertTag : function(tg)
24975     {
24976         // could be a bit smarter... -> wrap the current selected tRoo..
24977         if (tg.toLowerCase() == 'span' ||
24978             tg.toLowerCase() == 'code' ||
24979             tg.toLowerCase() == 'sup' ||
24980             tg.toLowerCase() == 'sub' 
24981             ) {
24982             
24983             range = this.createRange(this.getSelection());
24984             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24985             wrappingNode.appendChild(range.extractContents());
24986             range.insertNode(wrappingNode);
24987
24988             return;
24989             
24990             
24991             
24992         }
24993         this.execCmd("formatblock",   tg);
24994         
24995     },
24996     
24997     insertText : function(txt)
24998     {
24999         
25000         
25001         var range = this.createRange();
25002         range.deleteContents();
25003                //alert(Sender.getAttribute('label'));
25004                
25005         range.insertNode(this.doc.createTextNode(txt));
25006     } ,
25007     
25008      
25009
25010     /**
25011      * Executes a Midas editor command on the editor document and performs necessary focus and
25012      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25013      * @param {String} cmd The Midas command
25014      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25015      */
25016     relayCmd : function(cmd, value){
25017         this.win.focus();
25018         this.execCmd(cmd, value);
25019         this.owner.fireEvent('editorevent', this);
25020         //this.updateToolbar();
25021         this.owner.deferFocus();
25022     },
25023
25024     /**
25025      * Executes a Midas editor command directly on the editor document.
25026      * For visual commands, you should use {@link #relayCmd} instead.
25027      * <b>This should only be called after the editor is initialized.</b>
25028      * @param {String} cmd The Midas command
25029      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25030      */
25031     execCmd : function(cmd, value){
25032         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25033         this.syncValue();
25034     },
25035  
25036  
25037    
25038     /**
25039      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25040      * to insert tRoo.
25041      * @param {String} text | dom node.. 
25042      */
25043     insertAtCursor : function(text)
25044     {
25045         
25046         if(!this.activated){
25047             return;
25048         }
25049         /*
25050         if(Roo.isIE){
25051             this.win.focus();
25052             var r = this.doc.selection.createRange();
25053             if(r){
25054                 r.collapse(true);
25055                 r.pasteHTML(text);
25056                 this.syncValue();
25057                 this.deferFocus();
25058             
25059             }
25060             return;
25061         }
25062         */
25063         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25064             this.win.focus();
25065             
25066             
25067             // from jquery ui (MIT licenced)
25068             var range, node;
25069             var win = this.win;
25070             
25071             if (win.getSelection && win.getSelection().getRangeAt) {
25072                 range = win.getSelection().getRangeAt(0);
25073                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25074                 range.insertNode(node);
25075             } else if (win.document.selection && win.document.selection.createRange) {
25076                 // no firefox support
25077                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25078                 win.document.selection.createRange().pasteHTML(txt);
25079             } else {
25080                 // no firefox support
25081                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25082                 this.execCmd('InsertHTML', txt);
25083             } 
25084             
25085             this.syncValue();
25086             
25087             this.deferFocus();
25088         }
25089     },
25090  // private
25091     mozKeyPress : function(e){
25092         if(e.ctrlKey){
25093             var c = e.getCharCode(), cmd;
25094           
25095             if(c > 0){
25096                 c = String.fromCharCode(c).toLowerCase();
25097                 switch(c){
25098                     case 'b':
25099                         cmd = 'bold';
25100                         break;
25101                     case 'i':
25102                         cmd = 'italic';
25103                         break;
25104                     
25105                     case 'u':
25106                         cmd = 'underline';
25107                         break;
25108                     
25109                     case 'v':
25110                         this.cleanUpPaste.defer(100, this);
25111                         return;
25112                         
25113                 }
25114                 if(cmd){
25115                     this.win.focus();
25116                     this.execCmd(cmd);
25117                     this.deferFocus();
25118                     e.preventDefault();
25119                 }
25120                 
25121             }
25122         }
25123     },
25124
25125     // private
25126     fixKeys : function(){ // load time branching for fastest keydown performance
25127         if(Roo.isIE){
25128             return function(e){
25129                 var k = e.getKey(), r;
25130                 if(k == e.TAB){
25131                     e.stopEvent();
25132                     r = this.doc.selection.createRange();
25133                     if(r){
25134                         r.collapse(true);
25135                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25136                         this.deferFocus();
25137                     }
25138                     return;
25139                 }
25140                 
25141                 if(k == e.ENTER){
25142                     r = this.doc.selection.createRange();
25143                     if(r){
25144                         var target = r.parentElement();
25145                         if(!target || target.tagName.toLowerCase() != 'li'){
25146                             e.stopEvent();
25147                             r.pasteHTML('<br />');
25148                             r.collapse(false);
25149                             r.select();
25150                         }
25151                     }
25152                 }
25153                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25154                     this.cleanUpPaste.defer(100, this);
25155                     return;
25156                 }
25157                 
25158                 
25159             };
25160         }else if(Roo.isOpera){
25161             return function(e){
25162                 var k = e.getKey();
25163                 if(k == e.TAB){
25164                     e.stopEvent();
25165                     this.win.focus();
25166                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25167                     this.deferFocus();
25168                 }
25169                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25170                     this.cleanUpPaste.defer(100, this);
25171                     return;
25172                 }
25173                 
25174             };
25175         }else if(Roo.isSafari){
25176             return function(e){
25177                 var k = e.getKey();
25178                 
25179                 if(k == e.TAB){
25180                     e.stopEvent();
25181                     this.execCmd('InsertText','\t');
25182                     this.deferFocus();
25183                     return;
25184                 }
25185                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25186                     this.cleanUpPaste.defer(100, this);
25187                     return;
25188                 }
25189                 
25190              };
25191         }
25192     }(),
25193     
25194     getAllAncestors: function()
25195     {
25196         var p = this.getSelectedNode();
25197         var a = [];
25198         if (!p) {
25199             a.push(p); // push blank onto stack..
25200             p = this.getParentElement();
25201         }
25202         
25203         
25204         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25205             a.push(p);
25206             p = p.parentNode;
25207         }
25208         a.push(this.doc.body);
25209         return a;
25210     },
25211     lastSel : false,
25212     lastSelNode : false,
25213     
25214     
25215     getSelection : function() 
25216     {
25217         this.assignDocWin();
25218         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25219     },
25220     
25221     getSelectedNode: function() 
25222     {
25223         // this may only work on Gecko!!!
25224         
25225         // should we cache this!!!!
25226         
25227         
25228         
25229          
25230         var range = this.createRange(this.getSelection()).cloneRange();
25231         
25232         if (Roo.isIE) {
25233             var parent = range.parentElement();
25234             while (true) {
25235                 var testRange = range.duplicate();
25236                 testRange.moveToElementText(parent);
25237                 if (testRange.inRange(range)) {
25238                     break;
25239                 }
25240                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25241                     break;
25242                 }
25243                 parent = parent.parentElement;
25244             }
25245             return parent;
25246         }
25247         
25248         // is ancestor a text element.
25249         var ac =  range.commonAncestorContainer;
25250         if (ac.nodeType == 3) {
25251             ac = ac.parentNode;
25252         }
25253         
25254         var ar = ac.childNodes;
25255          
25256         var nodes = [];
25257         var other_nodes = [];
25258         var has_other_nodes = false;
25259         for (var i=0;i<ar.length;i++) {
25260             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25261                 continue;
25262             }
25263             // fullly contained node.
25264             
25265             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25266                 nodes.push(ar[i]);
25267                 continue;
25268             }
25269             
25270             // probably selected..
25271             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25272                 other_nodes.push(ar[i]);
25273                 continue;
25274             }
25275             // outer..
25276             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25277                 continue;
25278             }
25279             
25280             
25281             has_other_nodes = true;
25282         }
25283         if (!nodes.length && other_nodes.length) {
25284             nodes= other_nodes;
25285         }
25286         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25287             return false;
25288         }
25289         
25290         return nodes[0];
25291     },
25292     createRange: function(sel)
25293     {
25294         // this has strange effects when using with 
25295         // top toolbar - not sure if it's a great idea.
25296         //this.editor.contentWindow.focus();
25297         if (typeof sel != "undefined") {
25298             try {
25299                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25300             } catch(e) {
25301                 return this.doc.createRange();
25302             }
25303         } else {
25304             return this.doc.createRange();
25305         }
25306     },
25307     getParentElement: function()
25308     {
25309         
25310         this.assignDocWin();
25311         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25312         
25313         var range = this.createRange(sel);
25314          
25315         try {
25316             var p = range.commonAncestorContainer;
25317             while (p.nodeType == 3) { // text node
25318                 p = p.parentNode;
25319             }
25320             return p;
25321         } catch (e) {
25322             return null;
25323         }
25324     
25325     },
25326     /***
25327      *
25328      * Range intersection.. the hard stuff...
25329      *  '-1' = before
25330      *  '0' = hits..
25331      *  '1' = after.
25332      *         [ -- selected range --- ]
25333      *   [fail]                        [fail]
25334      *
25335      *    basically..
25336      *      if end is before start or  hits it. fail.
25337      *      if start is after end or hits it fail.
25338      *
25339      *   if either hits (but other is outside. - then it's not 
25340      *   
25341      *    
25342      **/
25343     
25344     
25345     // @see http://www.thismuchiknow.co.uk/?p=64.
25346     rangeIntersectsNode : function(range, node)
25347     {
25348         var nodeRange = node.ownerDocument.createRange();
25349         try {
25350             nodeRange.selectNode(node);
25351         } catch (e) {
25352             nodeRange.selectNodeContents(node);
25353         }
25354     
25355         var rangeStartRange = range.cloneRange();
25356         rangeStartRange.collapse(true);
25357     
25358         var rangeEndRange = range.cloneRange();
25359         rangeEndRange.collapse(false);
25360     
25361         var nodeStartRange = nodeRange.cloneRange();
25362         nodeStartRange.collapse(true);
25363     
25364         var nodeEndRange = nodeRange.cloneRange();
25365         nodeEndRange.collapse(false);
25366     
25367         return rangeStartRange.compareBoundaryPoints(
25368                  Range.START_TO_START, nodeEndRange) == -1 &&
25369                rangeEndRange.compareBoundaryPoints(
25370                  Range.START_TO_START, nodeStartRange) == 1;
25371         
25372          
25373     },
25374     rangeCompareNode : function(range, node)
25375     {
25376         var nodeRange = node.ownerDocument.createRange();
25377         try {
25378             nodeRange.selectNode(node);
25379         } catch (e) {
25380             nodeRange.selectNodeContents(node);
25381         }
25382         
25383         
25384         range.collapse(true);
25385     
25386         nodeRange.collapse(true);
25387      
25388         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25389         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25390          
25391         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25392         
25393         var nodeIsBefore   =  ss == 1;
25394         var nodeIsAfter    = ee == -1;
25395         
25396         if (nodeIsBefore && nodeIsAfter) {
25397             return 0; // outer
25398         }
25399         if (!nodeIsBefore && nodeIsAfter) {
25400             return 1; //right trailed.
25401         }
25402         
25403         if (nodeIsBefore && !nodeIsAfter) {
25404             return 2;  // left trailed.
25405         }
25406         // fully contined.
25407         return 3;
25408     },
25409
25410     // private? - in a new class?
25411     cleanUpPaste :  function()
25412     {
25413         // cleans up the whole document..
25414         Roo.log('cleanuppaste');
25415         
25416         this.cleanUpChildren(this.doc.body);
25417         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25418         if (clean != this.doc.body.innerHTML) {
25419             this.doc.body.innerHTML = clean;
25420         }
25421         
25422     },
25423     
25424     cleanWordChars : function(input) {// change the chars to hex code
25425         var he = Roo.HtmlEditorCore;
25426         
25427         var output = input;
25428         Roo.each(he.swapCodes, function(sw) { 
25429             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25430             
25431             output = output.replace(swapper, sw[1]);
25432         });
25433         
25434         return output;
25435     },
25436     
25437     
25438     cleanUpChildren : function (n)
25439     {
25440         if (!n.childNodes.length) {
25441             return;
25442         }
25443         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25444            this.cleanUpChild(n.childNodes[i]);
25445         }
25446     },
25447     
25448     
25449         
25450     
25451     cleanUpChild : function (node)
25452     {
25453         var ed = this;
25454         //console.log(node);
25455         if (node.nodeName == "#text") {
25456             // clean up silly Windows -- stuff?
25457             return; 
25458         }
25459         if (node.nodeName == "#comment") {
25460             node.parentNode.removeChild(node);
25461             // clean up silly Windows -- stuff?
25462             return; 
25463         }
25464         var lcname = node.tagName.toLowerCase();
25465         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25466         // whitelist of tags..
25467         
25468         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25469             // remove node.
25470             node.parentNode.removeChild(node);
25471             return;
25472             
25473         }
25474         
25475         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25476         
25477         // spans with no attributes - just remove them..
25478         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25479             remove_keep_children = true;
25480         }
25481         
25482         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25483         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25484         
25485         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25486         //    remove_keep_children = true;
25487         //}
25488         
25489         if (remove_keep_children) {
25490             this.cleanUpChildren(node);
25491             // inserts everything just before this node...
25492             while (node.childNodes.length) {
25493                 var cn = node.childNodes[0];
25494                 node.removeChild(cn);
25495                 node.parentNode.insertBefore(cn, node);
25496             }
25497             node.parentNode.removeChild(node);
25498             return;
25499         }
25500         
25501         if (!node.attributes || !node.attributes.length) {
25502             
25503           
25504             
25505             
25506             this.cleanUpChildren(node);
25507             return;
25508         }
25509         
25510         function cleanAttr(n,v)
25511         {
25512             
25513             if (v.match(/^\./) || v.match(/^\//)) {
25514                 return;
25515             }
25516             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25517                 return;
25518             }
25519             if (v.match(/^#/)) {
25520                 return;
25521             }
25522             if (v.match(/^\{/)) { // allow template editing.
25523                 return;
25524             }
25525 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25526             node.removeAttribute(n);
25527             
25528         }
25529         
25530         var cwhite = this.cwhite;
25531         var cblack = this.cblack;
25532             
25533         function cleanStyle(n,v)
25534         {
25535             if (v.match(/expression/)) { //XSS?? should we even bother..
25536                 node.removeAttribute(n);
25537                 return;
25538             }
25539             
25540             var parts = v.split(/;/);
25541             var clean = [];
25542             
25543             Roo.each(parts, function(p) {
25544                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25545                 if (!p.length) {
25546                     return true;
25547                 }
25548                 var l = p.split(':').shift().replace(/\s+/g,'');
25549                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25550                 
25551                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25552 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25553                     //node.removeAttribute(n);
25554                     return true;
25555                 }
25556                 //Roo.log()
25557                 // only allow 'c whitelisted system attributes'
25558                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25559 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25560                     //node.removeAttribute(n);
25561                     return true;
25562                 }
25563                 
25564                 
25565                  
25566                 
25567                 clean.push(p);
25568                 return true;
25569             });
25570             if (clean.length) { 
25571                 node.setAttribute(n, clean.join(';'));
25572             } else {
25573                 node.removeAttribute(n);
25574             }
25575             
25576         }
25577         
25578         
25579         for (var i = node.attributes.length-1; i > -1 ; i--) {
25580             var a = node.attributes[i];
25581             //console.log(a);
25582             
25583             if (a.name.toLowerCase().substr(0,2)=='on')  {
25584                 node.removeAttribute(a.name);
25585                 continue;
25586             }
25587             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25588                 node.removeAttribute(a.name);
25589                 continue;
25590             }
25591             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25592                 cleanAttr(a.name,a.value); // fixme..
25593                 continue;
25594             }
25595             if (a.name == 'style') {
25596                 cleanStyle(a.name,a.value);
25597                 continue;
25598             }
25599             /// clean up MS crap..
25600             // tecnically this should be a list of valid class'es..
25601             
25602             
25603             if (a.name == 'class') {
25604                 if (a.value.match(/^Mso/)) {
25605                     node.removeAttribute('class');
25606                 }
25607                 
25608                 if (a.value.match(/^body$/)) {
25609                     node.removeAttribute('class');
25610                 }
25611                 continue;
25612             }
25613             
25614             // style cleanup!?
25615             // class cleanup?
25616             
25617         }
25618         
25619         
25620         this.cleanUpChildren(node);
25621         
25622         
25623     },
25624     
25625     /**
25626      * Clean up MS wordisms...
25627      */
25628     cleanWord : function(node)
25629     {
25630         if (!node) {
25631             this.cleanWord(this.doc.body);
25632             return;
25633         }
25634         
25635         if(
25636                 node.nodeName == 'SPAN' &&
25637                 !node.hasAttributes() &&
25638                 node.childNodes.length == 1 &&
25639                 node.firstChild.nodeName == "#text"  
25640         ) {
25641             var textNode = node.firstChild;
25642             node.removeChild(textNode);
25643             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25644                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25645             }
25646             node.parentNode.insertBefore(textNode, node);
25647             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25648                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25649             }
25650             node.parentNode.removeChild(node);
25651         }
25652         
25653         if (node.nodeName == "#text") {
25654             // clean up silly Windows -- stuff?
25655             return; 
25656         }
25657         if (node.nodeName == "#comment") {
25658             node.parentNode.removeChild(node);
25659             // clean up silly Windows -- stuff?
25660             return; 
25661         }
25662         
25663         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25664             node.parentNode.removeChild(node);
25665             return;
25666         }
25667         //Roo.log(node.tagName);
25668         // remove - but keep children..
25669         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25670             //Roo.log('-- removed');
25671             while (node.childNodes.length) {
25672                 var cn = node.childNodes[0];
25673                 node.removeChild(cn);
25674                 node.parentNode.insertBefore(cn, node);
25675                 // move node to parent - and clean it..
25676                 this.cleanWord(cn);
25677             }
25678             node.parentNode.removeChild(node);
25679             /// no need to iterate chidlren = it's got none..
25680             //this.iterateChildren(node, this.cleanWord);
25681             return;
25682         }
25683         // clean styles
25684         if (node.className.length) {
25685             
25686             var cn = node.className.split(/\W+/);
25687             var cna = [];
25688             Roo.each(cn, function(cls) {
25689                 if (cls.match(/Mso[a-zA-Z]+/)) {
25690                     return;
25691                 }
25692                 cna.push(cls);
25693             });
25694             node.className = cna.length ? cna.join(' ') : '';
25695             if (!cna.length) {
25696                 node.removeAttribute("class");
25697             }
25698         }
25699         
25700         if (node.hasAttribute("lang")) {
25701             node.removeAttribute("lang");
25702         }
25703         
25704         if (node.hasAttribute("style")) {
25705             
25706             var styles = node.getAttribute("style").split(";");
25707             var nstyle = [];
25708             Roo.each(styles, function(s) {
25709                 if (!s.match(/:/)) {
25710                     return;
25711                 }
25712                 var kv = s.split(":");
25713                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25714                     return;
25715                 }
25716                 // what ever is left... we allow.
25717                 nstyle.push(s);
25718             });
25719             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25720             if (!nstyle.length) {
25721                 node.removeAttribute('style');
25722             }
25723         }
25724         this.iterateChildren(node, this.cleanWord);
25725         
25726         
25727         
25728     },
25729     /**
25730      * iterateChildren of a Node, calling fn each time, using this as the scole..
25731      * @param {DomNode} node node to iterate children of.
25732      * @param {Function} fn method of this class to call on each item.
25733      */
25734     iterateChildren : function(node, fn)
25735     {
25736         if (!node.childNodes.length) {
25737                 return;
25738         }
25739         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25740            fn.call(this, node.childNodes[i])
25741         }
25742     },
25743     
25744     
25745     /**
25746      * cleanTableWidths.
25747      *
25748      * Quite often pasting from word etc.. results in tables with column and widths.
25749      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25750      *
25751      */
25752     cleanTableWidths : function(node)
25753     {
25754          
25755          
25756         if (!node) {
25757             this.cleanTableWidths(this.doc.body);
25758             return;
25759         }
25760         
25761         // ignore list...
25762         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25763             return; 
25764         }
25765         Roo.log(node.tagName);
25766         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25767             this.iterateChildren(node, this.cleanTableWidths);
25768             return;
25769         }
25770         if (node.hasAttribute('width')) {
25771             node.removeAttribute('width');
25772         }
25773         
25774          
25775         if (node.hasAttribute("style")) {
25776             // pretty basic...
25777             
25778             var styles = node.getAttribute("style").split(";");
25779             var nstyle = [];
25780             Roo.each(styles, function(s) {
25781                 if (!s.match(/:/)) {
25782                     return;
25783                 }
25784                 var kv = s.split(":");
25785                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25786                     return;
25787                 }
25788                 // what ever is left... we allow.
25789                 nstyle.push(s);
25790             });
25791             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25792             if (!nstyle.length) {
25793                 node.removeAttribute('style');
25794             }
25795         }
25796         
25797         this.iterateChildren(node, this.cleanTableWidths);
25798         
25799         
25800     },
25801     
25802     
25803     
25804     
25805     domToHTML : function(currentElement, depth, nopadtext) {
25806         
25807         depth = depth || 0;
25808         nopadtext = nopadtext || false;
25809     
25810         if (!currentElement) {
25811             return this.domToHTML(this.doc.body);
25812         }
25813         
25814         //Roo.log(currentElement);
25815         var j;
25816         var allText = false;
25817         var nodeName = currentElement.nodeName;
25818         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25819         
25820         if  (nodeName == '#text') {
25821             
25822             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25823         }
25824         
25825         
25826         var ret = '';
25827         if (nodeName != 'BODY') {
25828              
25829             var i = 0;
25830             // Prints the node tagName, such as <A>, <IMG>, etc
25831             if (tagName) {
25832                 var attr = [];
25833                 for(i = 0; i < currentElement.attributes.length;i++) {
25834                     // quoting?
25835                     var aname = currentElement.attributes.item(i).name;
25836                     if (!currentElement.attributes.item(i).value.length) {
25837                         continue;
25838                     }
25839                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25840                 }
25841                 
25842                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25843             } 
25844             else {
25845                 
25846                 // eack
25847             }
25848         } else {
25849             tagName = false;
25850         }
25851         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25852             return ret;
25853         }
25854         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25855             nopadtext = true;
25856         }
25857         
25858         
25859         // Traverse the tree
25860         i = 0;
25861         var currentElementChild = currentElement.childNodes.item(i);
25862         var allText = true;
25863         var innerHTML  = '';
25864         lastnode = '';
25865         while (currentElementChild) {
25866             // Formatting code (indent the tree so it looks nice on the screen)
25867             var nopad = nopadtext;
25868             if (lastnode == 'SPAN') {
25869                 nopad  = true;
25870             }
25871             // text
25872             if  (currentElementChild.nodeName == '#text') {
25873                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25874                 toadd = nopadtext ? toadd : toadd.trim();
25875                 if (!nopad && toadd.length > 80) {
25876                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25877                 }
25878                 innerHTML  += toadd;
25879                 
25880                 i++;
25881                 currentElementChild = currentElement.childNodes.item(i);
25882                 lastNode = '';
25883                 continue;
25884             }
25885             allText = false;
25886             
25887             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25888                 
25889             // Recursively traverse the tree structure of the child node
25890             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25891             lastnode = currentElementChild.nodeName;
25892             i++;
25893             currentElementChild=currentElement.childNodes.item(i);
25894         }
25895         
25896         ret += innerHTML;
25897         
25898         if (!allText) {
25899                 // The remaining code is mostly for formatting the tree
25900             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25901         }
25902         
25903         
25904         if (tagName) {
25905             ret+= "</"+tagName+">";
25906         }
25907         return ret;
25908         
25909     },
25910         
25911     applyBlacklists : function()
25912     {
25913         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25914         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25915         
25916         this.white = [];
25917         this.black = [];
25918         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25919             if (b.indexOf(tag) > -1) {
25920                 return;
25921             }
25922             this.white.push(tag);
25923             
25924         }, this);
25925         
25926         Roo.each(w, function(tag) {
25927             if (b.indexOf(tag) > -1) {
25928                 return;
25929             }
25930             if (this.white.indexOf(tag) > -1) {
25931                 return;
25932             }
25933             this.white.push(tag);
25934             
25935         }, this);
25936         
25937         
25938         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25939             if (w.indexOf(tag) > -1) {
25940                 return;
25941             }
25942             this.black.push(tag);
25943             
25944         }, this);
25945         
25946         Roo.each(b, function(tag) {
25947             if (w.indexOf(tag) > -1) {
25948                 return;
25949             }
25950             if (this.black.indexOf(tag) > -1) {
25951                 return;
25952             }
25953             this.black.push(tag);
25954             
25955         }, this);
25956         
25957         
25958         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25959         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25960         
25961         this.cwhite = [];
25962         this.cblack = [];
25963         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25964             if (b.indexOf(tag) > -1) {
25965                 return;
25966             }
25967             this.cwhite.push(tag);
25968             
25969         }, this);
25970         
25971         Roo.each(w, function(tag) {
25972             if (b.indexOf(tag) > -1) {
25973                 return;
25974             }
25975             if (this.cwhite.indexOf(tag) > -1) {
25976                 return;
25977             }
25978             this.cwhite.push(tag);
25979             
25980         }, this);
25981         
25982         
25983         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25984             if (w.indexOf(tag) > -1) {
25985                 return;
25986             }
25987             this.cblack.push(tag);
25988             
25989         }, this);
25990         
25991         Roo.each(b, function(tag) {
25992             if (w.indexOf(tag) > -1) {
25993                 return;
25994             }
25995             if (this.cblack.indexOf(tag) > -1) {
25996                 return;
25997             }
25998             this.cblack.push(tag);
25999             
26000         }, this);
26001     },
26002     
26003     setStylesheets : function(stylesheets)
26004     {
26005         if(typeof(stylesheets) == 'string'){
26006             Roo.get(this.iframe.contentDocument.head).createChild({
26007                 tag : 'link',
26008                 rel : 'stylesheet',
26009                 type : 'text/css',
26010                 href : stylesheets
26011             });
26012             
26013             return;
26014         }
26015         var _this = this;
26016      
26017         Roo.each(stylesheets, function(s) {
26018             if(!s.length){
26019                 return;
26020             }
26021             
26022             Roo.get(_this.iframe.contentDocument.head).createChild({
26023                 tag : 'link',
26024                 rel : 'stylesheet',
26025                 type : 'text/css',
26026                 href : s
26027             });
26028         });
26029
26030         
26031     },
26032     
26033     removeStylesheets : function()
26034     {
26035         var _this = this;
26036         
26037         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26038             s.remove();
26039         });
26040     },
26041     
26042     setStyle : function(style)
26043     {
26044         Roo.get(this.iframe.contentDocument.head).createChild({
26045             tag : 'style',
26046             type : 'text/css',
26047             html : style
26048         });
26049
26050         return;
26051     }
26052     
26053     // hide stuff that is not compatible
26054     /**
26055      * @event blur
26056      * @hide
26057      */
26058     /**
26059      * @event change
26060      * @hide
26061      */
26062     /**
26063      * @event focus
26064      * @hide
26065      */
26066     /**
26067      * @event specialkey
26068      * @hide
26069      */
26070     /**
26071      * @cfg {String} fieldClass @hide
26072      */
26073     /**
26074      * @cfg {String} focusClass @hide
26075      */
26076     /**
26077      * @cfg {String} autoCreate @hide
26078      */
26079     /**
26080      * @cfg {String} inputType @hide
26081      */
26082     /**
26083      * @cfg {String} invalidClass @hide
26084      */
26085     /**
26086      * @cfg {String} invalidText @hide
26087      */
26088     /**
26089      * @cfg {String} msgFx @hide
26090      */
26091     /**
26092      * @cfg {String} validateOnBlur @hide
26093      */
26094 });
26095
26096 Roo.HtmlEditorCore.white = [
26097         'area', 'br', 'img', 'input', 'hr', 'wbr',
26098         
26099        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26100        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26101        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26102        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26103        'table',   'ul',         'xmp', 
26104        
26105        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26106       'thead',   'tr', 
26107      
26108       'dir', 'menu', 'ol', 'ul', 'dl',
26109        
26110       'embed',  'object'
26111 ];
26112
26113
26114 Roo.HtmlEditorCore.black = [
26115     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26116         'applet', // 
26117         'base',   'basefont', 'bgsound', 'blink',  'body', 
26118         'frame',  'frameset', 'head',    'html',   'ilayer', 
26119         'iframe', 'layer',  'link',     'meta',    'object',   
26120         'script', 'style' ,'title',  'xml' // clean later..
26121 ];
26122 Roo.HtmlEditorCore.clean = [
26123     'script', 'style', 'title', 'xml'
26124 ];
26125 Roo.HtmlEditorCore.remove = [
26126     'font'
26127 ];
26128 // attributes..
26129
26130 Roo.HtmlEditorCore.ablack = [
26131     'on'
26132 ];
26133     
26134 Roo.HtmlEditorCore.aclean = [ 
26135     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26136 ];
26137
26138 // protocols..
26139 Roo.HtmlEditorCore.pwhite= [
26140         'http',  'https',  'mailto'
26141 ];
26142
26143 // white listed style attributes.
26144 Roo.HtmlEditorCore.cwhite= [
26145       //  'text-align', /// default is to allow most things..
26146       
26147          
26148 //        'font-size'//??
26149 ];
26150
26151 // black listed style attributes.
26152 Roo.HtmlEditorCore.cblack= [
26153       //  'font-size' -- this can be set by the project 
26154 ];
26155
26156
26157 Roo.HtmlEditorCore.swapCodes   =[ 
26158     [    8211, "&#8211;" ], 
26159     [    8212, "&#8212;" ], 
26160     [    8216,  "'" ],  
26161     [    8217, "'" ],  
26162     [    8220, '"' ],  
26163     [    8221, '"' ],  
26164     [    8226, "*" ],  
26165     [    8230, "..." ]
26166 ]; 
26167
26168     /*
26169  * - LGPL
26170  *
26171  * HtmlEditor
26172  * 
26173  */
26174
26175 /**
26176  * @class Roo.bootstrap.HtmlEditor
26177  * @extends Roo.bootstrap.TextArea
26178  * Bootstrap HtmlEditor class
26179
26180  * @constructor
26181  * Create a new HtmlEditor
26182  * @param {Object} config The config object
26183  */
26184
26185 Roo.bootstrap.HtmlEditor = function(config){
26186     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26187     if (!this.toolbars) {
26188         this.toolbars = [];
26189     }
26190     
26191     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26192     this.addEvents({
26193             /**
26194              * @event initialize
26195              * Fires when the editor is fully initialized (including the iframe)
26196              * @param {HtmlEditor} this
26197              */
26198             initialize: true,
26199             /**
26200              * @event activate
26201              * Fires when the editor is first receives the focus. Any insertion must wait
26202              * until after this event.
26203              * @param {HtmlEditor} this
26204              */
26205             activate: true,
26206              /**
26207              * @event beforesync
26208              * Fires before the textarea is updated with content from the editor iframe. Return false
26209              * to cancel the sync.
26210              * @param {HtmlEditor} this
26211              * @param {String} html
26212              */
26213             beforesync: true,
26214              /**
26215              * @event beforepush
26216              * Fires before the iframe editor is updated with content from the textarea. Return false
26217              * to cancel the push.
26218              * @param {HtmlEditor} this
26219              * @param {String} html
26220              */
26221             beforepush: true,
26222              /**
26223              * @event sync
26224              * Fires when the textarea is updated with content from the editor iframe.
26225              * @param {HtmlEditor} this
26226              * @param {String} html
26227              */
26228             sync: true,
26229              /**
26230              * @event push
26231              * Fires when the iframe editor is updated with content from the textarea.
26232              * @param {HtmlEditor} this
26233              * @param {String} html
26234              */
26235             push: true,
26236              /**
26237              * @event editmodechange
26238              * Fires when the editor switches edit modes
26239              * @param {HtmlEditor} this
26240              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26241              */
26242             editmodechange: true,
26243             /**
26244              * @event editorevent
26245              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26246              * @param {HtmlEditor} this
26247              */
26248             editorevent: true,
26249             /**
26250              * @event firstfocus
26251              * Fires when on first focus - needed by toolbars..
26252              * @param {HtmlEditor} this
26253              */
26254             firstfocus: true,
26255             /**
26256              * @event autosave
26257              * Auto save the htmlEditor value as a file into Events
26258              * @param {HtmlEditor} this
26259              */
26260             autosave: true,
26261             /**
26262              * @event savedpreview
26263              * preview the saved version of htmlEditor
26264              * @param {HtmlEditor} this
26265              */
26266             savedpreview: true
26267         });
26268 };
26269
26270
26271 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26272     
26273     
26274       /**
26275      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26276      */
26277     toolbars : false,
26278     
26279      /**
26280     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26281     */
26282     btns : [],
26283    
26284      /**
26285      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26286      *                        Roo.resizable.
26287      */
26288     resizable : false,
26289      /**
26290      * @cfg {Number} height (in pixels)
26291      */   
26292     height: 300,
26293    /**
26294      * @cfg {Number} width (in pixels)
26295      */   
26296     width: false,
26297     
26298     /**
26299      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26300      * 
26301      */
26302     stylesheets: false,
26303     
26304     // id of frame..
26305     frameId: false,
26306     
26307     // private properties
26308     validationEvent : false,
26309     deferHeight: true,
26310     initialized : false,
26311     activated : false,
26312     
26313     onFocus : Roo.emptyFn,
26314     iframePad:3,
26315     hideMode:'offsets',
26316     
26317     tbContainer : false,
26318     
26319     bodyCls : '',
26320     
26321     toolbarContainer :function() {
26322         return this.wrap.select('.x-html-editor-tb',true).first();
26323     },
26324
26325     /**
26326      * Protected method that will not generally be called directly. It
26327      * is called when the editor creates its toolbar. Override this method if you need to
26328      * add custom toolbar buttons.
26329      * @param {HtmlEditor} editor
26330      */
26331     createToolbar : function(){
26332         Roo.log('renewing');
26333         Roo.log("create toolbars");
26334         
26335         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26336         this.toolbars[0].render(this.toolbarContainer());
26337         
26338         return;
26339         
26340 //        if (!editor.toolbars || !editor.toolbars.length) {
26341 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26342 //        }
26343 //        
26344 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26345 //            editor.toolbars[i] = Roo.factory(
26346 //                    typeof(editor.toolbars[i]) == 'string' ?
26347 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26348 //                Roo.bootstrap.HtmlEditor);
26349 //            editor.toolbars[i].init(editor);
26350 //        }
26351     },
26352
26353      
26354     // private
26355     onRender : function(ct, position)
26356     {
26357        // Roo.log("Call onRender: " + this.xtype);
26358         var _t = this;
26359         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26360       
26361         this.wrap = this.inputEl().wrap({
26362             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26363         });
26364         
26365         this.editorcore.onRender(ct, position);
26366          
26367         if (this.resizable) {
26368             this.resizeEl = new Roo.Resizable(this.wrap, {
26369                 pinned : true,
26370                 wrap: true,
26371                 dynamic : true,
26372                 minHeight : this.height,
26373                 height: this.height,
26374                 handles : this.resizable,
26375                 width: this.width,
26376                 listeners : {
26377                     resize : function(r, w, h) {
26378                         _t.onResize(w,h); // -something
26379                     }
26380                 }
26381             });
26382             
26383         }
26384         this.createToolbar(this);
26385        
26386         
26387         if(!this.width && this.resizable){
26388             this.setSize(this.wrap.getSize());
26389         }
26390         if (this.resizeEl) {
26391             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26392             // should trigger onReize..
26393         }
26394         
26395     },
26396
26397     // private
26398     onResize : function(w, h)
26399     {
26400         Roo.log('resize: ' +w + ',' + h );
26401         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26402         var ew = false;
26403         var eh = false;
26404         
26405         if(this.inputEl() ){
26406             if(typeof w == 'number'){
26407                 var aw = w - this.wrap.getFrameWidth('lr');
26408                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26409                 ew = aw;
26410             }
26411             if(typeof h == 'number'){
26412                  var tbh = -11;  // fixme it needs to tool bar size!
26413                 for (var i =0; i < this.toolbars.length;i++) {
26414                     // fixme - ask toolbars for heights?
26415                     tbh += this.toolbars[i].el.getHeight();
26416                     //if (this.toolbars[i].footer) {
26417                     //    tbh += this.toolbars[i].footer.el.getHeight();
26418                     //}
26419                 }
26420               
26421                 
26422                 
26423                 
26424                 
26425                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26426                 ah -= 5; // knock a few pixes off for look..
26427                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26428                 var eh = ah;
26429             }
26430         }
26431         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26432         this.editorcore.onResize(ew,eh);
26433         
26434     },
26435
26436     /**
26437      * Toggles the editor between standard and source edit mode.
26438      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26439      */
26440     toggleSourceEdit : function(sourceEditMode)
26441     {
26442         this.editorcore.toggleSourceEdit(sourceEditMode);
26443         
26444         if(this.editorcore.sourceEditMode){
26445             Roo.log('editor - showing textarea');
26446             
26447 //            Roo.log('in');
26448 //            Roo.log(this.syncValue());
26449             this.syncValue();
26450             this.inputEl().removeClass(['hide', 'x-hidden']);
26451             this.inputEl().dom.removeAttribute('tabIndex');
26452             this.inputEl().focus();
26453         }else{
26454             Roo.log('editor - hiding textarea');
26455 //            Roo.log('out')
26456 //            Roo.log(this.pushValue()); 
26457             this.pushValue();
26458             
26459             this.inputEl().addClass(['hide', 'x-hidden']);
26460             this.inputEl().dom.setAttribute('tabIndex', -1);
26461             //this.deferFocus();
26462         }
26463          
26464         if(this.resizable){
26465             this.setSize(this.wrap.getSize());
26466         }
26467         
26468         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26469     },
26470  
26471     // private (for BoxComponent)
26472     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26473
26474     // private (for BoxComponent)
26475     getResizeEl : function(){
26476         return this.wrap;
26477     },
26478
26479     // private (for BoxComponent)
26480     getPositionEl : function(){
26481         return this.wrap;
26482     },
26483
26484     // private
26485     initEvents : function(){
26486         this.originalValue = this.getValue();
26487     },
26488
26489 //    /**
26490 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26491 //     * @method
26492 //     */
26493 //    markInvalid : Roo.emptyFn,
26494 //    /**
26495 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26496 //     * @method
26497 //     */
26498 //    clearInvalid : Roo.emptyFn,
26499
26500     setValue : function(v){
26501         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26502         this.editorcore.pushValue();
26503     },
26504
26505      
26506     // private
26507     deferFocus : function(){
26508         this.focus.defer(10, this);
26509     },
26510
26511     // doc'ed in Field
26512     focus : function(){
26513         this.editorcore.focus();
26514         
26515     },
26516       
26517
26518     // private
26519     onDestroy : function(){
26520         
26521         
26522         
26523         if(this.rendered){
26524             
26525             for (var i =0; i < this.toolbars.length;i++) {
26526                 // fixme - ask toolbars for heights?
26527                 this.toolbars[i].onDestroy();
26528             }
26529             
26530             this.wrap.dom.innerHTML = '';
26531             this.wrap.remove();
26532         }
26533     },
26534
26535     // private
26536     onFirstFocus : function(){
26537         //Roo.log("onFirstFocus");
26538         this.editorcore.onFirstFocus();
26539          for (var i =0; i < this.toolbars.length;i++) {
26540             this.toolbars[i].onFirstFocus();
26541         }
26542         
26543     },
26544     
26545     // private
26546     syncValue : function()
26547     {   
26548         this.editorcore.syncValue();
26549     },
26550     
26551     pushValue : function()
26552     {   
26553         this.editorcore.pushValue();
26554     }
26555      
26556     
26557     // hide stuff that is not compatible
26558     /**
26559      * @event blur
26560      * @hide
26561      */
26562     /**
26563      * @event change
26564      * @hide
26565      */
26566     /**
26567      * @event focus
26568      * @hide
26569      */
26570     /**
26571      * @event specialkey
26572      * @hide
26573      */
26574     /**
26575      * @cfg {String} fieldClass @hide
26576      */
26577     /**
26578      * @cfg {String} focusClass @hide
26579      */
26580     /**
26581      * @cfg {String} autoCreate @hide
26582      */
26583     /**
26584      * @cfg {String} inputType @hide
26585      */
26586      
26587     /**
26588      * @cfg {String} invalidText @hide
26589      */
26590     /**
26591      * @cfg {String} msgFx @hide
26592      */
26593     /**
26594      * @cfg {String} validateOnBlur @hide
26595      */
26596 });
26597  
26598     
26599    
26600    
26601    
26602       
26603 Roo.namespace('Roo.bootstrap.htmleditor');
26604 /**
26605  * @class Roo.bootstrap.HtmlEditorToolbar1
26606  * Basic Toolbar
26607  * 
26608  * @example
26609  * Usage:
26610  *
26611  new Roo.bootstrap.HtmlEditor({
26612     ....
26613     toolbars : [
26614         new Roo.bootstrap.HtmlEditorToolbar1({
26615             disable : { fonts: 1 , format: 1, ..., ... , ...],
26616             btns : [ .... ]
26617         })
26618     }
26619      
26620  * 
26621  * @cfg {Object} disable List of elements to disable..
26622  * @cfg {Array} btns List of additional buttons.
26623  * 
26624  * 
26625  * NEEDS Extra CSS? 
26626  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26627  */
26628  
26629 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26630 {
26631     
26632     Roo.apply(this, config);
26633     
26634     // default disabled, based on 'good practice'..
26635     this.disable = this.disable || {};
26636     Roo.applyIf(this.disable, {
26637         fontSize : true,
26638         colors : true,
26639         specialElements : true
26640     });
26641     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26642     
26643     this.editor = config.editor;
26644     this.editorcore = config.editor.editorcore;
26645     
26646     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26647     
26648     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26649     // dont call parent... till later.
26650 }
26651 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26652      
26653     bar : true,
26654     
26655     editor : false,
26656     editorcore : false,
26657     
26658     
26659     formats : [
26660         "p" ,  
26661         "h1","h2","h3","h4","h5","h6", 
26662         "pre", "code", 
26663         "abbr", "acronym", "address", "cite", "samp", "var",
26664         'div','span'
26665     ],
26666     
26667     onRender : function(ct, position)
26668     {
26669        // Roo.log("Call onRender: " + this.xtype);
26670         
26671        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26672        Roo.log(this.el);
26673        this.el.dom.style.marginBottom = '0';
26674        var _this = this;
26675        var editorcore = this.editorcore;
26676        var editor= this.editor;
26677        
26678        var children = [];
26679        var btn = function(id,cmd , toggle, handler, html){
26680        
26681             var  event = toggle ? 'toggle' : 'click';
26682        
26683             var a = {
26684                 size : 'sm',
26685                 xtype: 'Button',
26686                 xns: Roo.bootstrap,
26687                 //glyphicon : id,
26688                 fa: id,
26689                 cmd : id || cmd,
26690                 enableToggle:toggle !== false,
26691                 html : html || '',
26692                 pressed : toggle ? false : null,
26693                 listeners : {}
26694             };
26695             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26696                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26697             };
26698             children.push(a);
26699             return a;
26700        }
26701        
26702     //    var cb_box = function...
26703         
26704         var style = {
26705                 xtype: 'Button',
26706                 size : 'sm',
26707                 xns: Roo.bootstrap,
26708                 fa : 'font',
26709                 //html : 'submit'
26710                 menu : {
26711                     xtype: 'Menu',
26712                     xns: Roo.bootstrap,
26713                     items:  []
26714                 }
26715         };
26716         Roo.each(this.formats, function(f) {
26717             style.menu.items.push({
26718                 xtype :'MenuItem',
26719                 xns: Roo.bootstrap,
26720                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26721                 tagname : f,
26722                 listeners : {
26723                     click : function()
26724                     {
26725                         editorcore.insertTag(this.tagname);
26726                         editor.focus();
26727                     }
26728                 }
26729                 
26730             });
26731         });
26732         children.push(style);   
26733         
26734         btn('bold',false,true);
26735         btn('italic',false,true);
26736         btn('align-left', 'justifyleft',true);
26737         btn('align-center', 'justifycenter',true);
26738         btn('align-right' , 'justifyright',true);
26739         btn('link', false, false, function(btn) {
26740             //Roo.log("create link?");
26741             var url = prompt(this.createLinkText, this.defaultLinkValue);
26742             if(url && url != 'http:/'+'/'){
26743                 this.editorcore.relayCmd('createlink', url);
26744             }
26745         }),
26746         btn('list','insertunorderedlist',true);
26747         btn('pencil', false,true, function(btn){
26748                 Roo.log(this);
26749                 this.toggleSourceEdit(btn.pressed);
26750         });
26751         
26752         if (this.editor.btns.length > 0) {
26753             for (var i = 0; i<this.editor.btns.length; i++) {
26754                 children.push(this.editor.btns[i]);
26755             }
26756         }
26757         
26758         /*
26759         var cog = {
26760                 xtype: 'Button',
26761                 size : 'sm',
26762                 xns: Roo.bootstrap,
26763                 glyphicon : 'cog',
26764                 //html : 'submit'
26765                 menu : {
26766                     xtype: 'Menu',
26767                     xns: Roo.bootstrap,
26768                     items:  []
26769                 }
26770         };
26771         
26772         cog.menu.items.push({
26773             xtype :'MenuItem',
26774             xns: Roo.bootstrap,
26775             html : Clean styles,
26776             tagname : f,
26777             listeners : {
26778                 click : function()
26779                 {
26780                     editorcore.insertTag(this.tagname);
26781                     editor.focus();
26782                 }
26783             }
26784             
26785         });
26786        */
26787         
26788          
26789        this.xtype = 'NavSimplebar';
26790         
26791         for(var i=0;i< children.length;i++) {
26792             
26793             this.buttons.add(this.addxtypeChild(children[i]));
26794             
26795         }
26796         
26797         editor.on('editorevent', this.updateToolbar, this);
26798     },
26799     onBtnClick : function(id)
26800     {
26801        this.editorcore.relayCmd(id);
26802        this.editorcore.focus();
26803     },
26804     
26805     /**
26806      * Protected method that will not generally be called directly. It triggers
26807      * a toolbar update by reading the markup state of the current selection in the editor.
26808      */
26809     updateToolbar: function(){
26810
26811         if(!this.editorcore.activated){
26812             this.editor.onFirstFocus(); // is this neeed?
26813             return;
26814         }
26815
26816         var btns = this.buttons; 
26817         var doc = this.editorcore.doc;
26818         btns.get('bold').setActive(doc.queryCommandState('bold'));
26819         btns.get('italic').setActive(doc.queryCommandState('italic'));
26820         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26821         
26822         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26823         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26824         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26825         
26826         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26827         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26828          /*
26829         
26830         var ans = this.editorcore.getAllAncestors();
26831         if (this.formatCombo) {
26832             
26833             
26834             var store = this.formatCombo.store;
26835             this.formatCombo.setValue("");
26836             for (var i =0; i < ans.length;i++) {
26837                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26838                     // select it..
26839                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26840                     break;
26841                 }
26842             }
26843         }
26844         
26845         
26846         
26847         // hides menus... - so this cant be on a menu...
26848         Roo.bootstrap.MenuMgr.hideAll();
26849         */
26850         Roo.bootstrap.MenuMgr.hideAll();
26851         //this.editorsyncValue();
26852     },
26853     onFirstFocus: function() {
26854         this.buttons.each(function(item){
26855            item.enable();
26856         });
26857     },
26858     toggleSourceEdit : function(sourceEditMode){
26859         
26860           
26861         if(sourceEditMode){
26862             Roo.log("disabling buttons");
26863            this.buttons.each( function(item){
26864                 if(item.cmd != 'pencil'){
26865                     item.disable();
26866                 }
26867             });
26868           
26869         }else{
26870             Roo.log("enabling buttons");
26871             if(this.editorcore.initialized){
26872                 this.buttons.each( function(item){
26873                     item.enable();
26874                 });
26875             }
26876             
26877         }
26878         Roo.log("calling toggole on editor");
26879         // tell the editor that it's been pressed..
26880         this.editor.toggleSourceEdit(sourceEditMode);
26881        
26882     }
26883 });
26884
26885
26886
26887
26888  
26889 /*
26890  * - LGPL
26891  */
26892
26893 /**
26894  * @class Roo.bootstrap.Markdown
26895  * @extends Roo.bootstrap.TextArea
26896  * Bootstrap Showdown editable area
26897  * @cfg {string} content
26898  * 
26899  * @constructor
26900  * Create a new Showdown
26901  */
26902
26903 Roo.bootstrap.Markdown = function(config){
26904     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26905    
26906 };
26907
26908 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26909     
26910     editing :false,
26911     
26912     initEvents : function()
26913     {
26914         
26915         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26916         this.markdownEl = this.el.createChild({
26917             cls : 'roo-markdown-area'
26918         });
26919         this.inputEl().addClass('d-none');
26920         if (this.getValue() == '') {
26921             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26922             
26923         } else {
26924             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26925         }
26926         this.markdownEl.on('click', this.toggleTextEdit, this);
26927         this.on('blur', this.toggleTextEdit, this);
26928         this.on('specialkey', this.resizeTextArea, this);
26929     },
26930     
26931     toggleTextEdit : function()
26932     {
26933         var sh = this.markdownEl.getHeight();
26934         this.inputEl().addClass('d-none');
26935         this.markdownEl.addClass('d-none');
26936         if (!this.editing) {
26937             // show editor?
26938             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26939             this.inputEl().removeClass('d-none');
26940             this.inputEl().focus();
26941             this.editing = true;
26942             return;
26943         }
26944         // show showdown...
26945         this.updateMarkdown();
26946         this.markdownEl.removeClass('d-none');
26947         this.editing = false;
26948         return;
26949     },
26950     updateMarkdown : function()
26951     {
26952         if (this.getValue() == '') {
26953             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26954             return;
26955         }
26956  
26957         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26958     },
26959     
26960     resizeTextArea: function () {
26961         
26962         var sh = 100;
26963         Roo.log([sh, this.getValue().split("\n").length * 30]);
26964         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26965     },
26966     setValue : function(val)
26967     {
26968         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26969         if (!this.editing) {
26970             this.updateMarkdown();
26971         }
26972         
26973     },
26974     focus : function()
26975     {
26976         if (!this.editing) {
26977             this.toggleTextEdit();
26978         }
26979         
26980     }
26981
26982
26983 });
26984 /**
26985  * @class Roo.bootstrap.Table.AbstractSelectionModel
26986  * @extends Roo.util.Observable
26987  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26988  * implemented by descendant classes.  This class should not be directly instantiated.
26989  * @constructor
26990  */
26991 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26992     this.locked = false;
26993     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26994 };
26995
26996
26997 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26998     /** @ignore Called by the grid automatically. Do not call directly. */
26999     init : function(grid){
27000         this.grid = grid;
27001         this.initEvents();
27002     },
27003
27004     /**
27005      * Locks the selections.
27006      */
27007     lock : function(){
27008         this.locked = true;
27009     },
27010
27011     /**
27012      * Unlocks the selections.
27013      */
27014     unlock : function(){
27015         this.locked = false;
27016     },
27017
27018     /**
27019      * Returns true if the selections are locked.
27020      * @return {Boolean}
27021      */
27022     isLocked : function(){
27023         return this.locked;
27024     },
27025     
27026     
27027     initEvents : function ()
27028     {
27029         
27030     }
27031 });
27032 /**
27033  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27034  * @class Roo.bootstrap.Table.RowSelectionModel
27035  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27036  * It supports multiple selections and keyboard selection/navigation. 
27037  * @constructor
27038  * @param {Object} config
27039  */
27040
27041 Roo.bootstrap.Table.RowSelectionModel = function(config){
27042     Roo.apply(this, config);
27043     this.selections = new Roo.util.MixedCollection(false, function(o){
27044         return o.id;
27045     });
27046
27047     this.last = false;
27048     this.lastActive = false;
27049
27050     this.addEvents({
27051         /**
27052              * @event selectionchange
27053              * Fires when the selection changes
27054              * @param {SelectionModel} this
27055              */
27056             "selectionchange" : true,
27057         /**
27058              * @event afterselectionchange
27059              * Fires after the selection changes (eg. by key press or clicking)
27060              * @param {SelectionModel} this
27061              */
27062             "afterselectionchange" : true,
27063         /**
27064              * @event beforerowselect
27065              * Fires when a row is selected being selected, return false to cancel.
27066              * @param {SelectionModel} this
27067              * @param {Number} rowIndex The selected index
27068              * @param {Boolean} keepExisting False if other selections will be cleared
27069              */
27070             "beforerowselect" : true,
27071         /**
27072              * @event rowselect
27073              * Fires when a row is selected.
27074              * @param {SelectionModel} this
27075              * @param {Number} rowIndex The selected index
27076              * @param {Roo.data.Record} r The record
27077              */
27078             "rowselect" : true,
27079         /**
27080              * @event rowdeselect
27081              * Fires when a row is deselected.
27082              * @param {SelectionModel} this
27083              * @param {Number} rowIndex The selected index
27084              */
27085         "rowdeselect" : true
27086     });
27087     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27088     this.locked = false;
27089  };
27090
27091 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27092     /**
27093      * @cfg {Boolean} singleSelect
27094      * True to allow selection of only one row at a time (defaults to false)
27095      */
27096     singleSelect : false,
27097
27098     // private
27099     initEvents : function()
27100     {
27101
27102         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27103         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27104         //}else{ // allow click to work like normal
27105          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27106         //}
27107         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27108         this.grid.on("rowclick", this.handleMouseDown, this);
27109         
27110         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27111             "up" : function(e){
27112                 if(!e.shiftKey){
27113                     this.selectPrevious(e.shiftKey);
27114                 }else if(this.last !== false && this.lastActive !== false){
27115                     var last = this.last;
27116                     this.selectRange(this.last,  this.lastActive-1);
27117                     this.grid.getView().focusRow(this.lastActive);
27118                     if(last !== false){
27119                         this.last = last;
27120                     }
27121                 }else{
27122                     this.selectFirstRow();
27123                 }
27124                 this.fireEvent("afterselectionchange", this);
27125             },
27126             "down" : function(e){
27127                 if(!e.shiftKey){
27128                     this.selectNext(e.shiftKey);
27129                 }else if(this.last !== false && this.lastActive !== false){
27130                     var last = this.last;
27131                     this.selectRange(this.last,  this.lastActive+1);
27132                     this.grid.getView().focusRow(this.lastActive);
27133                     if(last !== false){
27134                         this.last = last;
27135                     }
27136                 }else{
27137                     this.selectFirstRow();
27138                 }
27139                 this.fireEvent("afterselectionchange", this);
27140             },
27141             scope: this
27142         });
27143         this.grid.store.on('load', function(){
27144             this.selections.clear();
27145         },this);
27146         /*
27147         var view = this.grid.view;
27148         view.on("refresh", this.onRefresh, this);
27149         view.on("rowupdated", this.onRowUpdated, this);
27150         view.on("rowremoved", this.onRemove, this);
27151         */
27152     },
27153
27154     // private
27155     onRefresh : function()
27156     {
27157         var ds = this.grid.store, i, v = this.grid.view;
27158         var s = this.selections;
27159         s.each(function(r){
27160             if((i = ds.indexOfId(r.id)) != -1){
27161                 v.onRowSelect(i);
27162             }else{
27163                 s.remove(r);
27164             }
27165         });
27166     },
27167
27168     // private
27169     onRemove : function(v, index, r){
27170         this.selections.remove(r);
27171     },
27172
27173     // private
27174     onRowUpdated : function(v, index, r){
27175         if(this.isSelected(r)){
27176             v.onRowSelect(index);
27177         }
27178     },
27179
27180     /**
27181      * Select records.
27182      * @param {Array} records The records to select
27183      * @param {Boolean} keepExisting (optional) True to keep existing selections
27184      */
27185     selectRecords : function(records, keepExisting)
27186     {
27187         if(!keepExisting){
27188             this.clearSelections();
27189         }
27190             var ds = this.grid.store;
27191         for(var i = 0, len = records.length; i < len; i++){
27192             this.selectRow(ds.indexOf(records[i]), true);
27193         }
27194     },
27195
27196     /**
27197      * Gets the number of selected rows.
27198      * @return {Number}
27199      */
27200     getCount : function(){
27201         return this.selections.length;
27202     },
27203
27204     /**
27205      * Selects the first row in the grid.
27206      */
27207     selectFirstRow : function(){
27208         this.selectRow(0);
27209     },
27210
27211     /**
27212      * Select the last row.
27213      * @param {Boolean} keepExisting (optional) True to keep existing selections
27214      */
27215     selectLastRow : function(keepExisting){
27216         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27217         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27218     },
27219
27220     /**
27221      * Selects the row immediately following the last selected row.
27222      * @param {Boolean} keepExisting (optional) True to keep existing selections
27223      */
27224     selectNext : function(keepExisting)
27225     {
27226             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27227             this.selectRow(this.last+1, keepExisting);
27228             this.grid.getView().focusRow(this.last);
27229         }
27230     },
27231
27232     /**
27233      * Selects the row that precedes the last selected row.
27234      * @param {Boolean} keepExisting (optional) True to keep existing selections
27235      */
27236     selectPrevious : function(keepExisting){
27237         if(this.last){
27238             this.selectRow(this.last-1, keepExisting);
27239             this.grid.getView().focusRow(this.last);
27240         }
27241     },
27242
27243     /**
27244      * Returns the selected records
27245      * @return {Array} Array of selected records
27246      */
27247     getSelections : function(){
27248         return [].concat(this.selections.items);
27249     },
27250
27251     /**
27252      * Returns the first selected record.
27253      * @return {Record}
27254      */
27255     getSelected : function(){
27256         return this.selections.itemAt(0);
27257     },
27258
27259
27260     /**
27261      * Clears all selections.
27262      */
27263     clearSelections : function(fast)
27264     {
27265         if(this.locked) {
27266             return;
27267         }
27268         if(fast !== true){
27269                 var ds = this.grid.store;
27270             var s = this.selections;
27271             s.each(function(r){
27272                 this.deselectRow(ds.indexOfId(r.id));
27273             }, this);
27274             s.clear();
27275         }else{
27276             this.selections.clear();
27277         }
27278         this.last = false;
27279     },
27280
27281
27282     /**
27283      * Selects all rows.
27284      */
27285     selectAll : function(){
27286         if(this.locked) {
27287             return;
27288         }
27289         this.selections.clear();
27290         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27291             this.selectRow(i, true);
27292         }
27293     },
27294
27295     /**
27296      * Returns True if there is a selection.
27297      * @return {Boolean}
27298      */
27299     hasSelection : function(){
27300         return this.selections.length > 0;
27301     },
27302
27303     /**
27304      * Returns True if the specified row is selected.
27305      * @param {Number/Record} record The record or index of the record to check
27306      * @return {Boolean}
27307      */
27308     isSelected : function(index){
27309             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27310         return (r && this.selections.key(r.id) ? true : false);
27311     },
27312
27313     /**
27314      * Returns True if the specified record id is selected.
27315      * @param {String} id The id of record to check
27316      * @return {Boolean}
27317      */
27318     isIdSelected : function(id){
27319         return (this.selections.key(id) ? true : false);
27320     },
27321
27322
27323     // private
27324     handleMouseDBClick : function(e, t){
27325         
27326     },
27327     // private
27328     handleMouseDown : function(e, t)
27329     {
27330             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27331         if(this.isLocked() || rowIndex < 0 ){
27332             return;
27333         };
27334         if(e.shiftKey && this.last !== false){
27335             var last = this.last;
27336             this.selectRange(last, rowIndex, e.ctrlKey);
27337             this.last = last; // reset the last
27338             t.focus();
27339     
27340         }else{
27341             var isSelected = this.isSelected(rowIndex);
27342             //Roo.log("select row:" + rowIndex);
27343             if(isSelected){
27344                 this.deselectRow(rowIndex);
27345             } else {
27346                         this.selectRow(rowIndex, true);
27347             }
27348     
27349             /*
27350                 if(e.button !== 0 && isSelected){
27351                 alert('rowIndex 2: ' + rowIndex);
27352                     view.focusRow(rowIndex);
27353                 }else if(e.ctrlKey && isSelected){
27354                     this.deselectRow(rowIndex);
27355                 }else if(!isSelected){
27356                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27357                     view.focusRow(rowIndex);
27358                 }
27359             */
27360         }
27361         this.fireEvent("afterselectionchange", this);
27362     },
27363     // private
27364     handleDragableRowClick :  function(grid, rowIndex, e) 
27365     {
27366         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27367             this.selectRow(rowIndex, false);
27368             grid.view.focusRow(rowIndex);
27369              this.fireEvent("afterselectionchange", this);
27370         }
27371     },
27372     
27373     /**
27374      * Selects multiple rows.
27375      * @param {Array} rows Array of the indexes of the row to select
27376      * @param {Boolean} keepExisting (optional) True to keep existing selections
27377      */
27378     selectRows : function(rows, keepExisting){
27379         if(!keepExisting){
27380             this.clearSelections();
27381         }
27382         for(var i = 0, len = rows.length; i < len; i++){
27383             this.selectRow(rows[i], true);
27384         }
27385     },
27386
27387     /**
27388      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27389      * @param {Number} startRow The index of the first row in the range
27390      * @param {Number} endRow The index of the last row in the range
27391      * @param {Boolean} keepExisting (optional) True to retain existing selections
27392      */
27393     selectRange : function(startRow, endRow, keepExisting){
27394         if(this.locked) {
27395             return;
27396         }
27397         if(!keepExisting){
27398             this.clearSelections();
27399         }
27400         if(startRow <= endRow){
27401             for(var i = startRow; i <= endRow; i++){
27402                 this.selectRow(i, true);
27403             }
27404         }else{
27405             for(var i = startRow; i >= endRow; i--){
27406                 this.selectRow(i, true);
27407             }
27408         }
27409     },
27410
27411     /**
27412      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27413      * @param {Number} startRow The index of the first row in the range
27414      * @param {Number} endRow The index of the last row in the range
27415      */
27416     deselectRange : function(startRow, endRow, preventViewNotify){
27417         if(this.locked) {
27418             return;
27419         }
27420         for(var i = startRow; i <= endRow; i++){
27421             this.deselectRow(i, preventViewNotify);
27422         }
27423     },
27424
27425     /**
27426      * Selects a row.
27427      * @param {Number} row The index of the row to select
27428      * @param {Boolean} keepExisting (optional) True to keep existing selections
27429      */
27430     selectRow : function(index, keepExisting, preventViewNotify)
27431     {
27432             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27433             return;
27434         }
27435         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27436             if(!keepExisting || this.singleSelect){
27437                 this.clearSelections();
27438             }
27439             
27440             var r = this.grid.store.getAt(index);
27441             //console.log('selectRow - record id :' + r.id);
27442             
27443             this.selections.add(r);
27444             this.last = this.lastActive = index;
27445             if(!preventViewNotify){
27446                 var proxy = new Roo.Element(
27447                                 this.grid.getRowDom(index)
27448                 );
27449                 proxy.addClass('bg-info info');
27450             }
27451             this.fireEvent("rowselect", this, index, r);
27452             this.fireEvent("selectionchange", this);
27453         }
27454     },
27455
27456     /**
27457      * Deselects a row.
27458      * @param {Number} row The index of the row to deselect
27459      */
27460     deselectRow : function(index, preventViewNotify)
27461     {
27462         if(this.locked) {
27463             return;
27464         }
27465         if(this.last == index){
27466             this.last = false;
27467         }
27468         if(this.lastActive == index){
27469             this.lastActive = false;
27470         }
27471         
27472         var r = this.grid.store.getAt(index);
27473         if (!r) {
27474             return;
27475         }
27476         
27477         this.selections.remove(r);
27478         //.console.log('deselectRow - record id :' + r.id);
27479         if(!preventViewNotify){
27480         
27481             var proxy = new Roo.Element(
27482                 this.grid.getRowDom(index)
27483             );
27484             proxy.removeClass('bg-info info');
27485         }
27486         this.fireEvent("rowdeselect", this, index);
27487         this.fireEvent("selectionchange", this);
27488     },
27489
27490     // private
27491     restoreLast : function(){
27492         if(this._last){
27493             this.last = this._last;
27494         }
27495     },
27496
27497     // private
27498     acceptsNav : function(row, col, cm){
27499         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27500     },
27501
27502     // private
27503     onEditorKey : function(field, e){
27504         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27505         if(k == e.TAB){
27506             e.stopEvent();
27507             ed.completeEdit();
27508             if(e.shiftKey){
27509                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27510             }else{
27511                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27512             }
27513         }else if(k == e.ENTER && !e.ctrlKey){
27514             e.stopEvent();
27515             ed.completeEdit();
27516             if(e.shiftKey){
27517                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27518             }else{
27519                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27520             }
27521         }else if(k == e.ESC){
27522             ed.cancelEdit();
27523         }
27524         if(newCell){
27525             g.startEditing(newCell[0], newCell[1]);
27526         }
27527     }
27528 });
27529 /*
27530  * Based on:
27531  * Ext JS Library 1.1.1
27532  * Copyright(c) 2006-2007, Ext JS, LLC.
27533  *
27534  * Originally Released Under LGPL - original licence link has changed is not relivant.
27535  *
27536  * Fork - LGPL
27537  * <script type="text/javascript">
27538  */
27539  
27540 /**
27541  * @class Roo.bootstrap.PagingToolbar
27542  * @extends Roo.bootstrap.NavSimplebar
27543  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27544  * @constructor
27545  * Create a new PagingToolbar
27546  * @param {Object} config The config object
27547  * @param {Roo.data.Store} store
27548  */
27549 Roo.bootstrap.PagingToolbar = function(config)
27550 {
27551     // old args format still supported... - xtype is prefered..
27552         // created from xtype...
27553     
27554     this.ds = config.dataSource;
27555     
27556     if (config.store && !this.ds) {
27557         this.store= Roo.factory(config.store, Roo.data);
27558         this.ds = this.store;
27559         this.ds.xmodule = this.xmodule || false;
27560     }
27561     
27562     this.toolbarItems = [];
27563     if (config.items) {
27564         this.toolbarItems = config.items;
27565     }
27566     
27567     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27568     
27569     this.cursor = 0;
27570     
27571     if (this.ds) { 
27572         this.bind(this.ds);
27573     }
27574     
27575     if (Roo.bootstrap.version == 4) {
27576         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27577     } else {
27578         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27579     }
27580     
27581 };
27582
27583 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27584     /**
27585      * @cfg {Roo.data.Store} dataSource
27586      * The underlying data store providing the paged data
27587      */
27588     /**
27589      * @cfg {String/HTMLElement/Element} container
27590      * container The id or element that will contain the toolbar
27591      */
27592     /**
27593      * @cfg {Boolean} displayInfo
27594      * True to display the displayMsg (defaults to false)
27595      */
27596     /**
27597      * @cfg {Number} pageSize
27598      * The number of records to display per page (defaults to 20)
27599      */
27600     pageSize: 20,
27601     /**
27602      * @cfg {String} displayMsg
27603      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27604      */
27605     displayMsg : 'Displaying {0} - {1} of {2}',
27606     /**
27607      * @cfg {String} emptyMsg
27608      * The message to display when no records are found (defaults to "No data to display")
27609      */
27610     emptyMsg : 'No data to display',
27611     /**
27612      * Customizable piece of the default paging text (defaults to "Page")
27613      * @type String
27614      */
27615     beforePageText : "Page",
27616     /**
27617      * Customizable piece of the default paging text (defaults to "of %0")
27618      * @type String
27619      */
27620     afterPageText : "of {0}",
27621     /**
27622      * Customizable piece of the default paging text (defaults to "First Page")
27623      * @type String
27624      */
27625     firstText : "First Page",
27626     /**
27627      * Customizable piece of the default paging text (defaults to "Previous Page")
27628      * @type String
27629      */
27630     prevText : "Previous Page",
27631     /**
27632      * Customizable piece of the default paging text (defaults to "Next Page")
27633      * @type String
27634      */
27635     nextText : "Next Page",
27636     /**
27637      * Customizable piece of the default paging text (defaults to "Last Page")
27638      * @type String
27639      */
27640     lastText : "Last Page",
27641     /**
27642      * Customizable piece of the default paging text (defaults to "Refresh")
27643      * @type String
27644      */
27645     refreshText : "Refresh",
27646
27647     buttons : false,
27648     // private
27649     onRender : function(ct, position) 
27650     {
27651         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27652         this.navgroup.parentId = this.id;
27653         this.navgroup.onRender(this.el, null);
27654         // add the buttons to the navgroup
27655         
27656         if(this.displayInfo){
27657             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27658             this.displayEl = this.el.select('.x-paging-info', true).first();
27659 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27660 //            this.displayEl = navel.el.select('span',true).first();
27661         }
27662         
27663         var _this = this;
27664         
27665         if(this.buttons){
27666             Roo.each(_this.buttons, function(e){ // this might need to use render????
27667                Roo.factory(e).render(_this.el);
27668             });
27669         }
27670             
27671         Roo.each(_this.toolbarItems, function(e) {
27672             _this.navgroup.addItem(e);
27673         });
27674         
27675         
27676         this.first = this.navgroup.addItem({
27677             tooltip: this.firstText,
27678             cls: "prev btn-outline-secondary",
27679             html : ' <i class="fa fa-step-backward"></i>',
27680             disabled: true,
27681             preventDefault: true,
27682             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27683         });
27684         
27685         this.prev =  this.navgroup.addItem({
27686             tooltip: this.prevText,
27687             cls: "prev btn-outline-secondary",
27688             html : ' <i class="fa fa-backward"></i>',
27689             disabled: true,
27690             preventDefault: true,
27691             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27692         });
27693     //this.addSeparator();
27694         
27695         
27696         var field = this.navgroup.addItem( {
27697             tagtype : 'span',
27698             cls : 'x-paging-position  btn-outline-secondary',
27699              disabled: true,
27700             html : this.beforePageText  +
27701                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27702                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27703          } ); //?? escaped?
27704         
27705         this.field = field.el.select('input', true).first();
27706         this.field.on("keydown", this.onPagingKeydown, this);
27707         this.field.on("focus", function(){this.dom.select();});
27708     
27709     
27710         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27711         //this.field.setHeight(18);
27712         //this.addSeparator();
27713         this.next = this.navgroup.addItem({
27714             tooltip: this.nextText,
27715             cls: "next btn-outline-secondary",
27716             html : ' <i class="fa fa-forward"></i>',
27717             disabled: true,
27718             preventDefault: true,
27719             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27720         });
27721         this.last = this.navgroup.addItem({
27722             tooltip: this.lastText,
27723             html : ' <i class="fa fa-step-forward"></i>',
27724             cls: "next btn-outline-secondary",
27725             disabled: true,
27726             preventDefault: true,
27727             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27728         });
27729     //this.addSeparator();
27730         this.loading = this.navgroup.addItem({
27731             tooltip: this.refreshText,
27732             cls: "btn-outline-secondary",
27733             html : ' <i class="fa fa-refresh"></i>',
27734             preventDefault: true,
27735             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27736         });
27737         
27738     },
27739
27740     // private
27741     updateInfo : function(){
27742         if(this.displayEl){
27743             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27744             var msg = count == 0 ?
27745                 this.emptyMsg :
27746                 String.format(
27747                     this.displayMsg,
27748                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27749                 );
27750             this.displayEl.update(msg);
27751         }
27752     },
27753
27754     // private
27755     onLoad : function(ds, r, o)
27756     {
27757         this.cursor = o.params && o.params.start ? o.params.start : 0;
27758         
27759         var d = this.getPageData(),
27760             ap = d.activePage,
27761             ps = d.pages;
27762         
27763         
27764         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27765         this.field.dom.value = ap;
27766         this.first.setDisabled(ap == 1);
27767         this.prev.setDisabled(ap == 1);
27768         this.next.setDisabled(ap == ps);
27769         this.last.setDisabled(ap == ps);
27770         this.loading.enable();
27771         this.updateInfo();
27772     },
27773
27774     // private
27775     getPageData : function(){
27776         var total = this.ds.getTotalCount();
27777         return {
27778             total : total,
27779             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27780             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27781         };
27782     },
27783
27784     // private
27785     onLoadError : function(){
27786         this.loading.enable();
27787     },
27788
27789     // private
27790     onPagingKeydown : function(e){
27791         var k = e.getKey();
27792         var d = this.getPageData();
27793         if(k == e.RETURN){
27794             var v = this.field.dom.value, pageNum;
27795             if(!v || isNaN(pageNum = parseInt(v, 10))){
27796                 this.field.dom.value = d.activePage;
27797                 return;
27798             }
27799             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27800             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27801             e.stopEvent();
27802         }
27803         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))
27804         {
27805           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27806           this.field.dom.value = pageNum;
27807           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27808           e.stopEvent();
27809         }
27810         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27811         {
27812           var v = this.field.dom.value, pageNum; 
27813           var increment = (e.shiftKey) ? 10 : 1;
27814           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27815                 increment *= -1;
27816           }
27817           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27818             this.field.dom.value = d.activePage;
27819             return;
27820           }
27821           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27822           {
27823             this.field.dom.value = parseInt(v, 10) + increment;
27824             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27825             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27826           }
27827           e.stopEvent();
27828         }
27829     },
27830
27831     // private
27832     beforeLoad : function(){
27833         if(this.loading){
27834             this.loading.disable();
27835         }
27836     },
27837
27838     // private
27839     onClick : function(which){
27840         
27841         var ds = this.ds;
27842         if (!ds) {
27843             return;
27844         }
27845         
27846         switch(which){
27847             case "first":
27848                 ds.load({params:{start: 0, limit: this.pageSize}});
27849             break;
27850             case "prev":
27851                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27852             break;
27853             case "next":
27854                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27855             break;
27856             case "last":
27857                 var total = ds.getTotalCount();
27858                 var extra = total % this.pageSize;
27859                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27860                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27861             break;
27862             case "refresh":
27863                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27864             break;
27865         }
27866     },
27867
27868     /**
27869      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27870      * @param {Roo.data.Store} store The data store to unbind
27871      */
27872     unbind : function(ds){
27873         ds.un("beforeload", this.beforeLoad, this);
27874         ds.un("load", this.onLoad, this);
27875         ds.un("loadexception", this.onLoadError, this);
27876         ds.un("remove", this.updateInfo, this);
27877         ds.un("add", this.updateInfo, this);
27878         this.ds = undefined;
27879     },
27880
27881     /**
27882      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27883      * @param {Roo.data.Store} store The data store to bind
27884      */
27885     bind : function(ds){
27886         ds.on("beforeload", this.beforeLoad, this);
27887         ds.on("load", this.onLoad, this);
27888         ds.on("loadexception", this.onLoadError, this);
27889         ds.on("remove", this.updateInfo, this);
27890         ds.on("add", this.updateInfo, this);
27891         this.ds = ds;
27892     }
27893 });/*
27894  * - LGPL
27895  *
27896  * element
27897  * 
27898  */
27899
27900 /**
27901  * @class Roo.bootstrap.MessageBar
27902  * @extends Roo.bootstrap.Component
27903  * Bootstrap MessageBar class
27904  * @cfg {String} html contents of the MessageBar
27905  * @cfg {String} weight (info | success | warning | danger) default info
27906  * @cfg {String} beforeClass insert the bar before the given class
27907  * @cfg {Boolean} closable (true | false) default false
27908  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27909  * 
27910  * @constructor
27911  * Create a new Element
27912  * @param {Object} config The config object
27913  */
27914
27915 Roo.bootstrap.MessageBar = function(config){
27916     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27917 };
27918
27919 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27920     
27921     html: '',
27922     weight: 'info',
27923     closable: false,
27924     fixed: false,
27925     beforeClass: 'bootstrap-sticky-wrap',
27926     
27927     getAutoCreate : function(){
27928         
27929         var cfg = {
27930             tag: 'div',
27931             cls: 'alert alert-dismissable alert-' + this.weight,
27932             cn: [
27933                 {
27934                     tag: 'span',
27935                     cls: 'message',
27936                     html: this.html || ''
27937                 }
27938             ]
27939         };
27940         
27941         if(this.fixed){
27942             cfg.cls += ' alert-messages-fixed';
27943         }
27944         
27945         if(this.closable){
27946             cfg.cn.push({
27947                 tag: 'button',
27948                 cls: 'close',
27949                 html: 'x'
27950             });
27951         }
27952         
27953         return cfg;
27954     },
27955     
27956     onRender : function(ct, position)
27957     {
27958         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27959         
27960         if(!this.el){
27961             var cfg = Roo.apply({},  this.getAutoCreate());
27962             cfg.id = Roo.id();
27963             
27964             if (this.cls) {
27965                 cfg.cls += ' ' + this.cls;
27966             }
27967             if (this.style) {
27968                 cfg.style = this.style;
27969             }
27970             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27971             
27972             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27973         }
27974         
27975         this.el.select('>button.close').on('click', this.hide, this);
27976         
27977     },
27978     
27979     show : function()
27980     {
27981         if (!this.rendered) {
27982             this.render();
27983         }
27984         
27985         this.el.show();
27986         
27987         this.fireEvent('show', this);
27988         
27989     },
27990     
27991     hide : function()
27992     {
27993         if (!this.rendered) {
27994             this.render();
27995         }
27996         
27997         this.el.hide();
27998         
27999         this.fireEvent('hide', this);
28000     },
28001     
28002     update : function()
28003     {
28004 //        var e = this.el.dom.firstChild;
28005 //        
28006 //        if(this.closable){
28007 //            e = e.nextSibling;
28008 //        }
28009 //        
28010 //        e.data = this.html || '';
28011
28012         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28013     }
28014    
28015 });
28016
28017  
28018
28019      /*
28020  * - LGPL
28021  *
28022  * Graph
28023  * 
28024  */
28025
28026
28027 /**
28028  * @class Roo.bootstrap.Graph
28029  * @extends Roo.bootstrap.Component
28030  * Bootstrap Graph class
28031 > Prameters
28032  -sm {number} sm 4
28033  -md {number} md 5
28034  @cfg {String} graphtype  bar | vbar | pie
28035  @cfg {number} g_x coodinator | centre x (pie)
28036  @cfg {number} g_y coodinator | centre y (pie)
28037  @cfg {number} g_r radius (pie)
28038  @cfg {number} g_height height of the chart (respected by all elements in the set)
28039  @cfg {number} g_width width of the chart (respected by all elements in the set)
28040  @cfg {Object} title The title of the chart
28041     
28042  -{Array}  values
28043  -opts (object) options for the chart 
28044      o {
28045      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28046      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28047      o vgutter (number)
28048      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.
28049      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28050      o to
28051      o stretch (boolean)
28052      o }
28053  -opts (object) options for the pie
28054      o{
28055      o cut
28056      o startAngle (number)
28057      o endAngle (number)
28058      } 
28059  *
28060  * @constructor
28061  * Create a new Input
28062  * @param {Object} config The config object
28063  */
28064
28065 Roo.bootstrap.Graph = function(config){
28066     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28067     
28068     this.addEvents({
28069         // img events
28070         /**
28071          * @event click
28072          * The img click event for the img.
28073          * @param {Roo.EventObject} e
28074          */
28075         "click" : true
28076     });
28077 };
28078
28079 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28080     
28081     sm: 4,
28082     md: 5,
28083     graphtype: 'bar',
28084     g_height: 250,
28085     g_width: 400,
28086     g_x: 50,
28087     g_y: 50,
28088     g_r: 30,
28089     opts:{
28090         //g_colors: this.colors,
28091         g_type: 'soft',
28092         g_gutter: '20%'
28093
28094     },
28095     title : false,
28096
28097     getAutoCreate : function(){
28098         
28099         var cfg = {
28100             tag: 'div',
28101             html : null
28102         };
28103         
28104         
28105         return  cfg;
28106     },
28107
28108     onRender : function(ct,position){
28109         
28110         
28111         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28112         
28113         if (typeof(Raphael) == 'undefined') {
28114             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28115             return;
28116         }
28117         
28118         this.raphael = Raphael(this.el.dom);
28119         
28120                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28121                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28122                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28123                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28124                 /*
28125                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28126                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28127                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28128                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28129                 
28130                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28131                 r.barchart(330, 10, 300, 220, data1);
28132                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28133                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28134                 */
28135                 
28136                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28137                 // r.barchart(30, 30, 560, 250,  xdata, {
28138                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28139                 //     axis : "0 0 1 1",
28140                 //     axisxlabels :  xdata
28141                 //     //yvalues : cols,
28142                    
28143                 // });
28144 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28145 //        
28146 //        this.load(null,xdata,{
28147 //                axis : "0 0 1 1",
28148 //                axisxlabels :  xdata
28149 //                });
28150
28151     },
28152
28153     load : function(graphtype,xdata,opts)
28154     {
28155         this.raphael.clear();
28156         if(!graphtype) {
28157             graphtype = this.graphtype;
28158         }
28159         if(!opts){
28160             opts = this.opts;
28161         }
28162         var r = this.raphael,
28163             fin = function () {
28164                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28165             },
28166             fout = function () {
28167                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28168             },
28169             pfin = function() {
28170                 this.sector.stop();
28171                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28172
28173                 if (this.label) {
28174                     this.label[0].stop();
28175                     this.label[0].attr({ r: 7.5 });
28176                     this.label[1].attr({ "font-weight": 800 });
28177                 }
28178             },
28179             pfout = function() {
28180                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28181
28182                 if (this.label) {
28183                     this.label[0].animate({ r: 5 }, 500, "bounce");
28184                     this.label[1].attr({ "font-weight": 400 });
28185                 }
28186             };
28187
28188         switch(graphtype){
28189             case 'bar':
28190                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28191                 break;
28192             case 'hbar':
28193                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28194                 break;
28195             case 'pie':
28196 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28197 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28198 //            
28199                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28200                 
28201                 break;
28202
28203         }
28204         
28205         if(this.title){
28206             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28207         }
28208         
28209     },
28210     
28211     setTitle: function(o)
28212     {
28213         this.title = o;
28214     },
28215     
28216     initEvents: function() {
28217         
28218         if(!this.href){
28219             this.el.on('click', this.onClick, this);
28220         }
28221     },
28222     
28223     onClick : function(e)
28224     {
28225         Roo.log('img onclick');
28226         this.fireEvent('click', this, e);
28227     }
28228    
28229 });
28230
28231  
28232 /*
28233  * - LGPL
28234  *
28235  * numberBox
28236  * 
28237  */
28238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28239
28240 /**
28241  * @class Roo.bootstrap.dash.NumberBox
28242  * @extends Roo.bootstrap.Component
28243  * Bootstrap NumberBox class
28244  * @cfg {String} headline Box headline
28245  * @cfg {String} content Box content
28246  * @cfg {String} icon Box icon
28247  * @cfg {String} footer Footer text
28248  * @cfg {String} fhref Footer href
28249  * 
28250  * @constructor
28251  * Create a new NumberBox
28252  * @param {Object} config The config object
28253  */
28254
28255
28256 Roo.bootstrap.dash.NumberBox = function(config){
28257     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28258     
28259 };
28260
28261 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28262     
28263     headline : '',
28264     content : '',
28265     icon : '',
28266     footer : '',
28267     fhref : '',
28268     ficon : '',
28269     
28270     getAutoCreate : function(){
28271         
28272         var cfg = {
28273             tag : 'div',
28274             cls : 'small-box ',
28275             cn : [
28276                 {
28277                     tag : 'div',
28278                     cls : 'inner',
28279                     cn :[
28280                         {
28281                             tag : 'h3',
28282                             cls : 'roo-headline',
28283                             html : this.headline
28284                         },
28285                         {
28286                             tag : 'p',
28287                             cls : 'roo-content',
28288                             html : this.content
28289                         }
28290                     ]
28291                 }
28292             ]
28293         };
28294         
28295         if(this.icon){
28296             cfg.cn.push({
28297                 tag : 'div',
28298                 cls : 'icon',
28299                 cn :[
28300                     {
28301                         tag : 'i',
28302                         cls : 'ion ' + this.icon
28303                     }
28304                 ]
28305             });
28306         }
28307         
28308         if(this.footer){
28309             var footer = {
28310                 tag : 'a',
28311                 cls : 'small-box-footer',
28312                 href : this.fhref || '#',
28313                 html : this.footer
28314             };
28315             
28316             cfg.cn.push(footer);
28317             
28318         }
28319         
28320         return  cfg;
28321     },
28322
28323     onRender : function(ct,position){
28324         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28325
28326
28327        
28328                 
28329     },
28330
28331     setHeadline: function (value)
28332     {
28333         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28334     },
28335     
28336     setFooter: function (value, href)
28337     {
28338         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28339         
28340         if(href){
28341             this.el.select('a.small-box-footer',true).first().attr('href', href);
28342         }
28343         
28344     },
28345
28346     setContent: function (value)
28347     {
28348         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28349     },
28350
28351     initEvents: function() 
28352     {   
28353         
28354     }
28355     
28356 });
28357
28358  
28359 /*
28360  * - LGPL
28361  *
28362  * TabBox
28363  * 
28364  */
28365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28366
28367 /**
28368  * @class Roo.bootstrap.dash.TabBox
28369  * @extends Roo.bootstrap.Component
28370  * Bootstrap TabBox class
28371  * @cfg {String} title Title of the TabBox
28372  * @cfg {String} icon Icon of the TabBox
28373  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28374  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28375  * 
28376  * @constructor
28377  * Create a new TabBox
28378  * @param {Object} config The config object
28379  */
28380
28381
28382 Roo.bootstrap.dash.TabBox = function(config){
28383     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28384     this.addEvents({
28385         // raw events
28386         /**
28387          * @event addpane
28388          * When a pane is added
28389          * @param {Roo.bootstrap.dash.TabPane} pane
28390          */
28391         "addpane" : true,
28392         /**
28393          * @event activatepane
28394          * When a pane is activated
28395          * @param {Roo.bootstrap.dash.TabPane} pane
28396          */
28397         "activatepane" : true
28398         
28399          
28400     });
28401     
28402     this.panes = [];
28403 };
28404
28405 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28406
28407     title : '',
28408     icon : false,
28409     showtabs : true,
28410     tabScrollable : false,
28411     
28412     getChildContainer : function()
28413     {
28414         return this.el.select('.tab-content', true).first();
28415     },
28416     
28417     getAutoCreate : function(){
28418         
28419         var header = {
28420             tag: 'li',
28421             cls: 'pull-left header',
28422             html: this.title,
28423             cn : []
28424         };
28425         
28426         if(this.icon){
28427             header.cn.push({
28428                 tag: 'i',
28429                 cls: 'fa ' + this.icon
28430             });
28431         }
28432         
28433         var h = {
28434             tag: 'ul',
28435             cls: 'nav nav-tabs pull-right',
28436             cn: [
28437                 header
28438             ]
28439         };
28440         
28441         if(this.tabScrollable){
28442             h = {
28443                 tag: 'div',
28444                 cls: 'tab-header',
28445                 cn: [
28446                     {
28447                         tag: 'ul',
28448                         cls: 'nav nav-tabs pull-right',
28449                         cn: [
28450                             header
28451                         ]
28452                     }
28453                 ]
28454             };
28455         }
28456         
28457         var cfg = {
28458             tag: 'div',
28459             cls: 'nav-tabs-custom',
28460             cn: [
28461                 h,
28462                 {
28463                     tag: 'div',
28464                     cls: 'tab-content no-padding',
28465                     cn: []
28466                 }
28467             ]
28468         };
28469
28470         return  cfg;
28471     },
28472     initEvents : function()
28473     {
28474         //Roo.log('add add pane handler');
28475         this.on('addpane', this.onAddPane, this);
28476     },
28477      /**
28478      * Updates the box title
28479      * @param {String} html to set the title to.
28480      */
28481     setTitle : function(value)
28482     {
28483         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28484     },
28485     onAddPane : function(pane)
28486     {
28487         this.panes.push(pane);
28488         //Roo.log('addpane');
28489         //Roo.log(pane);
28490         // tabs are rendere left to right..
28491         if(!this.showtabs){
28492             return;
28493         }
28494         
28495         var ctr = this.el.select('.nav-tabs', true).first();
28496          
28497          
28498         var existing = ctr.select('.nav-tab',true);
28499         var qty = existing.getCount();;
28500         
28501         
28502         var tab = ctr.createChild({
28503             tag : 'li',
28504             cls : 'nav-tab' + (qty ? '' : ' active'),
28505             cn : [
28506                 {
28507                     tag : 'a',
28508                     href:'#',
28509                     html : pane.title
28510                 }
28511             ]
28512         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28513         pane.tab = tab;
28514         
28515         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28516         if (!qty) {
28517             pane.el.addClass('active');
28518         }
28519         
28520                 
28521     },
28522     onTabClick : function(ev,un,ob,pane)
28523     {
28524         //Roo.log('tab - prev default');
28525         ev.preventDefault();
28526         
28527         
28528         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28529         pane.tab.addClass('active');
28530         //Roo.log(pane.title);
28531         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28532         // technically we should have a deactivate event.. but maybe add later.
28533         // and it should not de-activate the selected tab...
28534         this.fireEvent('activatepane', pane);
28535         pane.el.addClass('active');
28536         pane.fireEvent('activate');
28537         
28538         
28539     },
28540     
28541     getActivePane : function()
28542     {
28543         var r = false;
28544         Roo.each(this.panes, function(p) {
28545             if(p.el.hasClass('active')){
28546                 r = p;
28547                 return false;
28548             }
28549             
28550             return;
28551         });
28552         
28553         return r;
28554     }
28555     
28556     
28557 });
28558
28559  
28560 /*
28561  * - LGPL
28562  *
28563  * Tab pane
28564  * 
28565  */
28566 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28567 /**
28568  * @class Roo.bootstrap.TabPane
28569  * @extends Roo.bootstrap.Component
28570  * Bootstrap TabPane class
28571  * @cfg {Boolean} active (false | true) Default false
28572  * @cfg {String} title title of panel
28573
28574  * 
28575  * @constructor
28576  * Create a new TabPane
28577  * @param {Object} config The config object
28578  */
28579
28580 Roo.bootstrap.dash.TabPane = function(config){
28581     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28582     
28583     this.addEvents({
28584         // raw events
28585         /**
28586          * @event activate
28587          * When a pane is activated
28588          * @param {Roo.bootstrap.dash.TabPane} pane
28589          */
28590         "activate" : true
28591          
28592     });
28593 };
28594
28595 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28596     
28597     active : false,
28598     title : '',
28599     
28600     // the tabBox that this is attached to.
28601     tab : false,
28602      
28603     getAutoCreate : function() 
28604     {
28605         var cfg = {
28606             tag: 'div',
28607             cls: 'tab-pane'
28608         };
28609         
28610         if(this.active){
28611             cfg.cls += ' active';
28612         }
28613         
28614         return cfg;
28615     },
28616     initEvents  : function()
28617     {
28618         //Roo.log('trigger add pane handler');
28619         this.parent().fireEvent('addpane', this)
28620     },
28621     
28622      /**
28623      * Updates the tab title 
28624      * @param {String} html to set the title to.
28625      */
28626     setTitle: function(str)
28627     {
28628         if (!this.tab) {
28629             return;
28630         }
28631         this.title = str;
28632         this.tab.select('a', true).first().dom.innerHTML = str;
28633         
28634     }
28635     
28636     
28637     
28638 });
28639
28640  
28641
28642
28643  /*
28644  * - LGPL
28645  *
28646  * menu
28647  * 
28648  */
28649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28650
28651 /**
28652  * @class Roo.bootstrap.menu.Menu
28653  * @extends Roo.bootstrap.Component
28654  * Bootstrap Menu class - container for Menu
28655  * @cfg {String} html Text of the menu
28656  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28657  * @cfg {String} icon Font awesome icon
28658  * @cfg {String} pos Menu align to (top | bottom) default bottom
28659  * 
28660  * 
28661  * @constructor
28662  * Create a new Menu
28663  * @param {Object} config The config object
28664  */
28665
28666
28667 Roo.bootstrap.menu.Menu = function(config){
28668     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28669     
28670     this.addEvents({
28671         /**
28672          * @event beforeshow
28673          * Fires before this menu is displayed
28674          * @param {Roo.bootstrap.menu.Menu} this
28675          */
28676         beforeshow : true,
28677         /**
28678          * @event beforehide
28679          * Fires before this menu is hidden
28680          * @param {Roo.bootstrap.menu.Menu} this
28681          */
28682         beforehide : true,
28683         /**
28684          * @event show
28685          * Fires after this menu is displayed
28686          * @param {Roo.bootstrap.menu.Menu} this
28687          */
28688         show : true,
28689         /**
28690          * @event hide
28691          * Fires after this menu is hidden
28692          * @param {Roo.bootstrap.menu.Menu} this
28693          */
28694         hide : true,
28695         /**
28696          * @event click
28697          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28698          * @param {Roo.bootstrap.menu.Menu} this
28699          * @param {Roo.EventObject} e
28700          */
28701         click : true
28702     });
28703     
28704 };
28705
28706 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28707     
28708     submenu : false,
28709     html : '',
28710     weight : 'default',
28711     icon : false,
28712     pos : 'bottom',
28713     
28714     
28715     getChildContainer : function() {
28716         if(this.isSubMenu){
28717             return this.el;
28718         }
28719         
28720         return this.el.select('ul.dropdown-menu', true).first();  
28721     },
28722     
28723     getAutoCreate : function()
28724     {
28725         var text = [
28726             {
28727                 tag : 'span',
28728                 cls : 'roo-menu-text',
28729                 html : this.html
28730             }
28731         ];
28732         
28733         if(this.icon){
28734             text.unshift({
28735                 tag : 'i',
28736                 cls : 'fa ' + this.icon
28737             })
28738         }
28739         
28740         
28741         var cfg = {
28742             tag : 'div',
28743             cls : 'btn-group',
28744             cn : [
28745                 {
28746                     tag : 'button',
28747                     cls : 'dropdown-button btn btn-' + this.weight,
28748                     cn : text
28749                 },
28750                 {
28751                     tag : 'button',
28752                     cls : 'dropdown-toggle btn btn-' + this.weight,
28753                     cn : [
28754                         {
28755                             tag : 'span',
28756                             cls : 'caret'
28757                         }
28758                     ]
28759                 },
28760                 {
28761                     tag : 'ul',
28762                     cls : 'dropdown-menu'
28763                 }
28764             ]
28765             
28766         };
28767         
28768         if(this.pos == 'top'){
28769             cfg.cls += ' dropup';
28770         }
28771         
28772         if(this.isSubMenu){
28773             cfg = {
28774                 tag : 'ul',
28775                 cls : 'dropdown-menu'
28776             }
28777         }
28778         
28779         return cfg;
28780     },
28781     
28782     onRender : function(ct, position)
28783     {
28784         this.isSubMenu = ct.hasClass('dropdown-submenu');
28785         
28786         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28787     },
28788     
28789     initEvents : function() 
28790     {
28791         if(this.isSubMenu){
28792             return;
28793         }
28794         
28795         this.hidden = true;
28796         
28797         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28798         this.triggerEl.on('click', this.onTriggerPress, this);
28799         
28800         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28801         this.buttonEl.on('click', this.onClick, this);
28802         
28803     },
28804     
28805     list : function()
28806     {
28807         if(this.isSubMenu){
28808             return this.el;
28809         }
28810         
28811         return this.el.select('ul.dropdown-menu', true).first();
28812     },
28813     
28814     onClick : function(e)
28815     {
28816         this.fireEvent("click", this, e);
28817     },
28818     
28819     onTriggerPress  : function(e)
28820     {   
28821         if (this.isVisible()) {
28822             this.hide();
28823         } else {
28824             this.show();
28825         }
28826     },
28827     
28828     isVisible : function(){
28829         return !this.hidden;
28830     },
28831     
28832     show : function()
28833     {
28834         this.fireEvent("beforeshow", this);
28835         
28836         this.hidden = false;
28837         this.el.addClass('open');
28838         
28839         Roo.get(document).on("mouseup", this.onMouseUp, this);
28840         
28841         this.fireEvent("show", this);
28842         
28843         
28844     },
28845     
28846     hide : function()
28847     {
28848         this.fireEvent("beforehide", this);
28849         
28850         this.hidden = true;
28851         this.el.removeClass('open');
28852         
28853         Roo.get(document).un("mouseup", this.onMouseUp);
28854         
28855         this.fireEvent("hide", this);
28856     },
28857     
28858     onMouseUp : function()
28859     {
28860         this.hide();
28861     }
28862     
28863 });
28864
28865  
28866  /*
28867  * - LGPL
28868  *
28869  * menu item
28870  * 
28871  */
28872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28873
28874 /**
28875  * @class Roo.bootstrap.menu.Item
28876  * @extends Roo.bootstrap.Component
28877  * Bootstrap MenuItem class
28878  * @cfg {Boolean} submenu (true | false) default false
28879  * @cfg {String} html text of the item
28880  * @cfg {String} href the link
28881  * @cfg {Boolean} disable (true | false) default false
28882  * @cfg {Boolean} preventDefault (true | false) default true
28883  * @cfg {String} icon Font awesome icon
28884  * @cfg {String} pos Submenu align to (left | right) default right 
28885  * 
28886  * 
28887  * @constructor
28888  * Create a new Item
28889  * @param {Object} config The config object
28890  */
28891
28892
28893 Roo.bootstrap.menu.Item = function(config){
28894     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28895     this.addEvents({
28896         /**
28897          * @event mouseover
28898          * Fires when the mouse is hovering over this menu
28899          * @param {Roo.bootstrap.menu.Item} this
28900          * @param {Roo.EventObject} e
28901          */
28902         mouseover : true,
28903         /**
28904          * @event mouseout
28905          * Fires when the mouse exits this menu
28906          * @param {Roo.bootstrap.menu.Item} this
28907          * @param {Roo.EventObject} e
28908          */
28909         mouseout : true,
28910         // raw events
28911         /**
28912          * @event click
28913          * The raw click event for the entire grid.
28914          * @param {Roo.EventObject} e
28915          */
28916         click : true
28917     });
28918 };
28919
28920 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28921     
28922     submenu : false,
28923     href : '',
28924     html : '',
28925     preventDefault: true,
28926     disable : false,
28927     icon : false,
28928     pos : 'right',
28929     
28930     getAutoCreate : function()
28931     {
28932         var text = [
28933             {
28934                 tag : 'span',
28935                 cls : 'roo-menu-item-text',
28936                 html : this.html
28937             }
28938         ];
28939         
28940         if(this.icon){
28941             text.unshift({
28942                 tag : 'i',
28943                 cls : 'fa ' + this.icon
28944             })
28945         }
28946         
28947         var cfg = {
28948             tag : 'li',
28949             cn : [
28950                 {
28951                     tag : 'a',
28952                     href : this.href || '#',
28953                     cn : text
28954                 }
28955             ]
28956         };
28957         
28958         if(this.disable){
28959             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28960         }
28961         
28962         if(this.submenu){
28963             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28964             
28965             if(this.pos == 'left'){
28966                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28967             }
28968         }
28969         
28970         return cfg;
28971     },
28972     
28973     initEvents : function() 
28974     {
28975         this.el.on('mouseover', this.onMouseOver, this);
28976         this.el.on('mouseout', this.onMouseOut, this);
28977         
28978         this.el.select('a', true).first().on('click', this.onClick, this);
28979         
28980     },
28981     
28982     onClick : function(e)
28983     {
28984         if(this.preventDefault){
28985             e.preventDefault();
28986         }
28987         
28988         this.fireEvent("click", this, e);
28989     },
28990     
28991     onMouseOver : function(e)
28992     {
28993         if(this.submenu && this.pos == 'left'){
28994             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28995         }
28996         
28997         this.fireEvent("mouseover", this, e);
28998     },
28999     
29000     onMouseOut : function(e)
29001     {
29002         this.fireEvent("mouseout", this, e);
29003     }
29004 });
29005
29006  
29007
29008  /*
29009  * - LGPL
29010  *
29011  * menu separator
29012  * 
29013  */
29014 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29015
29016 /**
29017  * @class Roo.bootstrap.menu.Separator
29018  * @extends Roo.bootstrap.Component
29019  * Bootstrap Separator class
29020  * 
29021  * @constructor
29022  * Create a new Separator
29023  * @param {Object} config The config object
29024  */
29025
29026
29027 Roo.bootstrap.menu.Separator = function(config){
29028     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29029 };
29030
29031 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29032     
29033     getAutoCreate : function(){
29034         var cfg = {
29035             tag : 'li',
29036             cls: 'dropdown-divider divider'
29037         };
29038         
29039         return cfg;
29040     }
29041    
29042 });
29043
29044  
29045
29046  /*
29047  * - LGPL
29048  *
29049  * Tooltip
29050  * 
29051  */
29052
29053 /**
29054  * @class Roo.bootstrap.Tooltip
29055  * Bootstrap Tooltip class
29056  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29057  * to determine which dom element triggers the tooltip.
29058  * 
29059  * It needs to add support for additional attributes like tooltip-position
29060  * 
29061  * @constructor
29062  * Create a new Toolti
29063  * @param {Object} config The config object
29064  */
29065
29066 Roo.bootstrap.Tooltip = function(config){
29067     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29068     
29069     this.alignment = Roo.bootstrap.Tooltip.alignment;
29070     
29071     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29072         this.alignment = config.alignment;
29073     }
29074     
29075 };
29076
29077 Roo.apply(Roo.bootstrap.Tooltip, {
29078     /**
29079      * @function init initialize tooltip monitoring.
29080      * @static
29081      */
29082     currentEl : false,
29083     currentTip : false,
29084     currentRegion : false,
29085     
29086     //  init : delay?
29087     
29088     init : function()
29089     {
29090         Roo.get(document).on('mouseover', this.enter ,this);
29091         Roo.get(document).on('mouseout', this.leave, this);
29092          
29093         
29094         this.currentTip = new Roo.bootstrap.Tooltip();
29095     },
29096     
29097     enter : function(ev)
29098     {
29099         var dom = ev.getTarget();
29100         
29101         //Roo.log(['enter',dom]);
29102         var el = Roo.fly(dom);
29103         if (this.currentEl) {
29104             //Roo.log(dom);
29105             //Roo.log(this.currentEl);
29106             //Roo.log(this.currentEl.contains(dom));
29107             if (this.currentEl == el) {
29108                 return;
29109             }
29110             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29111                 return;
29112             }
29113
29114         }
29115         
29116         if (this.currentTip.el) {
29117             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29118         }    
29119         //Roo.log(ev);
29120         
29121         if(!el || el.dom == document){
29122             return;
29123         }
29124         
29125         var bindEl = el; 
29126         var pel = false;
29127         if (!el.attr('tooltip')) {
29128             pel = el.findParent("[tooltip]");
29129             if (pel) {
29130                 bindEl = Roo.get(pel);
29131             }
29132         }
29133         
29134        
29135         
29136         // you can not look for children, as if el is the body.. then everythign is the child..
29137         if (!pel && !el.attr('tooltip')) { //
29138             if (!el.select("[tooltip]").elements.length) {
29139                 return;
29140             }
29141             // is the mouse over this child...?
29142             bindEl = el.select("[tooltip]").first();
29143             var xy = ev.getXY();
29144             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29145                 //Roo.log("not in region.");
29146                 return;
29147             }
29148             //Roo.log("child element over..");
29149             
29150         }
29151         this.currentEl = el;
29152         this.currentTip.bind(bindEl);
29153         this.currentRegion = Roo.lib.Region.getRegion(dom);
29154         this.currentTip.enter();
29155         
29156     },
29157     leave : function(ev)
29158     {
29159         var dom = ev.getTarget();
29160         //Roo.log(['leave',dom]);
29161         if (!this.currentEl) {
29162             return;
29163         }
29164         
29165         
29166         if (dom != this.currentEl.dom) {
29167             return;
29168         }
29169         var xy = ev.getXY();
29170         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29171             return;
29172         }
29173         // only activate leave if mouse cursor is outside... bounding box..
29174         
29175         
29176         
29177         
29178         if (this.currentTip) {
29179             this.currentTip.leave();
29180         }
29181         //Roo.log('clear currentEl');
29182         this.currentEl = false;
29183         
29184         
29185     },
29186     alignment : {
29187         'left' : ['r-l', [-2,0], 'right'],
29188         'right' : ['l-r', [2,0], 'left'],
29189         'bottom' : ['t-b', [0,2], 'top'],
29190         'top' : [ 'b-t', [0,-2], 'bottom']
29191     }
29192     
29193 });
29194
29195
29196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29197     
29198     
29199     bindEl : false,
29200     
29201     delay : null, // can be { show : 300 , hide: 500}
29202     
29203     timeout : null,
29204     
29205     hoverState : null, //???
29206     
29207     placement : 'bottom', 
29208     
29209     alignment : false,
29210     
29211     getAutoCreate : function(){
29212     
29213         var cfg = {
29214            cls : 'tooltip',   
29215            role : 'tooltip',
29216            cn : [
29217                 {
29218                     cls : 'tooltip-arrow arrow'
29219                 },
29220                 {
29221                     cls : 'tooltip-inner'
29222                 }
29223            ]
29224         };
29225         
29226         return cfg;
29227     },
29228     bind : function(el)
29229     {
29230         this.bindEl = el;
29231     },
29232     
29233     initEvents : function()
29234     {
29235         this.arrowEl = this.el.select('.arrow', true).first();
29236         this.innerEl = this.el.select('.tooltip-inner', true).first();
29237     },
29238     
29239     enter : function () {
29240        
29241         if (this.timeout != null) {
29242             clearTimeout(this.timeout);
29243         }
29244         
29245         this.hoverState = 'in';
29246          //Roo.log("enter - show");
29247         if (!this.delay || !this.delay.show) {
29248             this.show();
29249             return;
29250         }
29251         var _t = this;
29252         this.timeout = setTimeout(function () {
29253             if (_t.hoverState == 'in') {
29254                 _t.show();
29255             }
29256         }, this.delay.show);
29257     },
29258     leave : function()
29259     {
29260         clearTimeout(this.timeout);
29261     
29262         this.hoverState = 'out';
29263          if (!this.delay || !this.delay.hide) {
29264             this.hide();
29265             return;
29266         }
29267        
29268         var _t = this;
29269         this.timeout = setTimeout(function () {
29270             //Roo.log("leave - timeout");
29271             
29272             if (_t.hoverState == 'out') {
29273                 _t.hide();
29274                 Roo.bootstrap.Tooltip.currentEl = false;
29275             }
29276         }, delay);
29277     },
29278     
29279     show : function (msg)
29280     {
29281         if (!this.el) {
29282             this.render(document.body);
29283         }
29284         // set content.
29285         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29286         
29287         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29288         
29289         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29290         
29291         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29292                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29293         
29294         var placement = typeof this.placement == 'function' ?
29295             this.placement.call(this, this.el, on_el) :
29296             this.placement;
29297             
29298         var autoToken = /\s?auto?\s?/i;
29299         var autoPlace = autoToken.test(placement);
29300         if (autoPlace) {
29301             placement = placement.replace(autoToken, '') || 'top';
29302         }
29303         
29304         //this.el.detach()
29305         //this.el.setXY([0,0]);
29306         this.el.show();
29307         //this.el.dom.style.display='block';
29308         
29309         //this.el.appendTo(on_el);
29310         
29311         var p = this.getPosition();
29312         var box = this.el.getBox();
29313         
29314         if (autoPlace) {
29315             // fixme..
29316         }
29317         
29318         var align = this.alignment[placement];
29319         
29320         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29321         
29322         if(placement == 'top' || placement == 'bottom'){
29323             if(xy[0] < 0){
29324                 placement = 'right';
29325             }
29326             
29327             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29328                 placement = 'left';
29329             }
29330             
29331             var scroll = Roo.select('body', true).first().getScroll();
29332             
29333             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29334                 placement = 'top';
29335             }
29336             
29337             align = this.alignment[placement];
29338             
29339             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29340             
29341         }
29342         
29343         var elems = document.getElementsByTagName('div');
29344         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29345         for (var i = 0; i < elems.length; i++) {
29346           var zindex = Number.parseInt(
29347                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29348                 10
29349           );
29350           if (zindex > highest) {
29351             highest = zindex;
29352           }
29353         }
29354         
29355         
29356         
29357         this.el.dom.style.zIndex = highest;
29358         
29359         this.el.alignTo(this.bindEl, align[0],align[1]);
29360         //var arrow = this.el.select('.arrow',true).first();
29361         //arrow.set(align[2], 
29362         
29363         this.el.addClass(placement);
29364         this.el.addClass("bs-tooltip-"+ placement);
29365         
29366         this.el.addClass('in fade show');
29367         
29368         this.hoverState = null;
29369         
29370         if (this.el.hasClass('fade')) {
29371             // fade it?
29372         }
29373         
29374         
29375         
29376         
29377         
29378     },
29379     hide : function()
29380     {
29381          
29382         if (!this.el) {
29383             return;
29384         }
29385         //this.el.setXY([0,0]);
29386         this.el.removeClass(['show', 'in']);
29387         //this.el.hide();
29388         
29389     }
29390     
29391 });
29392  
29393
29394  /*
29395  * - LGPL
29396  *
29397  * Location Picker
29398  * 
29399  */
29400
29401 /**
29402  * @class Roo.bootstrap.LocationPicker
29403  * @extends Roo.bootstrap.Component
29404  * Bootstrap LocationPicker class
29405  * @cfg {Number} latitude Position when init default 0
29406  * @cfg {Number} longitude Position when init default 0
29407  * @cfg {Number} zoom default 15
29408  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29409  * @cfg {Boolean} mapTypeControl default false
29410  * @cfg {Boolean} disableDoubleClickZoom default false
29411  * @cfg {Boolean} scrollwheel default true
29412  * @cfg {Boolean} streetViewControl default false
29413  * @cfg {Number} radius default 0
29414  * @cfg {String} locationName
29415  * @cfg {Boolean} draggable default true
29416  * @cfg {Boolean} enableAutocomplete default false
29417  * @cfg {Boolean} enableReverseGeocode default true
29418  * @cfg {String} markerTitle
29419  * 
29420  * @constructor
29421  * Create a new LocationPicker
29422  * @param {Object} config The config object
29423  */
29424
29425
29426 Roo.bootstrap.LocationPicker = function(config){
29427     
29428     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29429     
29430     this.addEvents({
29431         /**
29432          * @event initial
29433          * Fires when the picker initialized.
29434          * @param {Roo.bootstrap.LocationPicker} this
29435          * @param {Google Location} location
29436          */
29437         initial : true,
29438         /**
29439          * @event positionchanged
29440          * Fires when the picker position changed.
29441          * @param {Roo.bootstrap.LocationPicker} this
29442          * @param {Google Location} location
29443          */
29444         positionchanged : true,
29445         /**
29446          * @event resize
29447          * Fires when the map resize.
29448          * @param {Roo.bootstrap.LocationPicker} this
29449          */
29450         resize : true,
29451         /**
29452          * @event show
29453          * Fires when the map show.
29454          * @param {Roo.bootstrap.LocationPicker} this
29455          */
29456         show : true,
29457         /**
29458          * @event hide
29459          * Fires when the map hide.
29460          * @param {Roo.bootstrap.LocationPicker} this
29461          */
29462         hide : true,
29463         /**
29464          * @event mapClick
29465          * Fires when click the map.
29466          * @param {Roo.bootstrap.LocationPicker} this
29467          * @param {Map event} e
29468          */
29469         mapClick : true,
29470         /**
29471          * @event mapRightClick
29472          * Fires when right click the map.
29473          * @param {Roo.bootstrap.LocationPicker} this
29474          * @param {Map event} e
29475          */
29476         mapRightClick : true,
29477         /**
29478          * @event markerClick
29479          * Fires when click the marker.
29480          * @param {Roo.bootstrap.LocationPicker} this
29481          * @param {Map event} e
29482          */
29483         markerClick : true,
29484         /**
29485          * @event markerRightClick
29486          * Fires when right click the marker.
29487          * @param {Roo.bootstrap.LocationPicker} this
29488          * @param {Map event} e
29489          */
29490         markerRightClick : true,
29491         /**
29492          * @event OverlayViewDraw
29493          * Fires when OverlayView Draw
29494          * @param {Roo.bootstrap.LocationPicker} this
29495          */
29496         OverlayViewDraw : true,
29497         /**
29498          * @event OverlayViewOnAdd
29499          * Fires when OverlayView Draw
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          */
29502         OverlayViewOnAdd : true,
29503         /**
29504          * @event OverlayViewOnRemove
29505          * Fires when OverlayView Draw
29506          * @param {Roo.bootstrap.LocationPicker} this
29507          */
29508         OverlayViewOnRemove : true,
29509         /**
29510          * @event OverlayViewShow
29511          * Fires when OverlayView Draw
29512          * @param {Roo.bootstrap.LocationPicker} this
29513          * @param {Pixel} cpx
29514          */
29515         OverlayViewShow : true,
29516         /**
29517          * @event OverlayViewHide
29518          * Fires when OverlayView Draw
29519          * @param {Roo.bootstrap.LocationPicker} this
29520          */
29521         OverlayViewHide : true,
29522         /**
29523          * @event loadexception
29524          * Fires when load google lib failed.
29525          * @param {Roo.bootstrap.LocationPicker} this
29526          */
29527         loadexception : true
29528     });
29529         
29530 };
29531
29532 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29533     
29534     gMapContext: false,
29535     
29536     latitude: 0,
29537     longitude: 0,
29538     zoom: 15,
29539     mapTypeId: false,
29540     mapTypeControl: false,
29541     disableDoubleClickZoom: false,
29542     scrollwheel: true,
29543     streetViewControl: false,
29544     radius: 0,
29545     locationName: '',
29546     draggable: true,
29547     enableAutocomplete: false,
29548     enableReverseGeocode: true,
29549     markerTitle: '',
29550     
29551     getAutoCreate: function()
29552     {
29553
29554         var cfg = {
29555             tag: 'div',
29556             cls: 'roo-location-picker'
29557         };
29558         
29559         return cfg
29560     },
29561     
29562     initEvents: function(ct, position)
29563     {       
29564         if(!this.el.getWidth() || this.isApplied()){
29565             return;
29566         }
29567         
29568         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29569         
29570         this.initial();
29571     },
29572     
29573     initial: function()
29574     {
29575         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29576             this.fireEvent('loadexception', this);
29577             return;
29578         }
29579         
29580         if(!this.mapTypeId){
29581             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29582         }
29583         
29584         this.gMapContext = this.GMapContext();
29585         
29586         this.initOverlayView();
29587         
29588         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29589         
29590         var _this = this;
29591                 
29592         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29593             _this.setPosition(_this.gMapContext.marker.position);
29594         });
29595         
29596         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29597             _this.fireEvent('mapClick', this, event);
29598             
29599         });
29600
29601         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29602             _this.fireEvent('mapRightClick', this, event);
29603             
29604         });
29605         
29606         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29607             _this.fireEvent('markerClick', this, event);
29608             
29609         });
29610
29611         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29612             _this.fireEvent('markerRightClick', this, event);
29613             
29614         });
29615         
29616         this.setPosition(this.gMapContext.location);
29617         
29618         this.fireEvent('initial', this, this.gMapContext.location);
29619     },
29620     
29621     initOverlayView: function()
29622     {
29623         var _this = this;
29624         
29625         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29626             
29627             draw: function()
29628             {
29629                 _this.fireEvent('OverlayViewDraw', _this);
29630             },
29631             
29632             onAdd: function()
29633             {
29634                 _this.fireEvent('OverlayViewOnAdd', _this);
29635             },
29636             
29637             onRemove: function()
29638             {
29639                 _this.fireEvent('OverlayViewOnRemove', _this);
29640             },
29641             
29642             show: function(cpx)
29643             {
29644                 _this.fireEvent('OverlayViewShow', _this, cpx);
29645             },
29646             
29647             hide: function()
29648             {
29649                 _this.fireEvent('OverlayViewHide', _this);
29650             }
29651             
29652         });
29653     },
29654     
29655     fromLatLngToContainerPixel: function(event)
29656     {
29657         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29658     },
29659     
29660     isApplied: function() 
29661     {
29662         return this.getGmapContext() == false ? false : true;
29663     },
29664     
29665     getGmapContext: function() 
29666     {
29667         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29668     },
29669     
29670     GMapContext: function() 
29671     {
29672         var position = new google.maps.LatLng(this.latitude, this.longitude);
29673         
29674         var _map = new google.maps.Map(this.el.dom, {
29675             center: position,
29676             zoom: this.zoom,
29677             mapTypeId: this.mapTypeId,
29678             mapTypeControl: this.mapTypeControl,
29679             disableDoubleClickZoom: this.disableDoubleClickZoom,
29680             scrollwheel: this.scrollwheel,
29681             streetViewControl: this.streetViewControl,
29682             locationName: this.locationName,
29683             draggable: this.draggable,
29684             enableAutocomplete: this.enableAutocomplete,
29685             enableReverseGeocode: this.enableReverseGeocode
29686         });
29687         
29688         var _marker = new google.maps.Marker({
29689             position: position,
29690             map: _map,
29691             title: this.markerTitle,
29692             draggable: this.draggable
29693         });
29694         
29695         return {
29696             map: _map,
29697             marker: _marker,
29698             circle: null,
29699             location: position,
29700             radius: this.radius,
29701             locationName: this.locationName,
29702             addressComponents: {
29703                 formatted_address: null,
29704                 addressLine1: null,
29705                 addressLine2: null,
29706                 streetName: null,
29707                 streetNumber: null,
29708                 city: null,
29709                 district: null,
29710                 state: null,
29711                 stateOrProvince: null
29712             },
29713             settings: this,
29714             domContainer: this.el.dom,
29715             geodecoder: new google.maps.Geocoder()
29716         };
29717     },
29718     
29719     drawCircle: function(center, radius, options) 
29720     {
29721         if (this.gMapContext.circle != null) {
29722             this.gMapContext.circle.setMap(null);
29723         }
29724         if (radius > 0) {
29725             radius *= 1;
29726             options = Roo.apply({}, options, {
29727                 strokeColor: "#0000FF",
29728                 strokeOpacity: .35,
29729                 strokeWeight: 2,
29730                 fillColor: "#0000FF",
29731                 fillOpacity: .2
29732             });
29733             
29734             options.map = this.gMapContext.map;
29735             options.radius = radius;
29736             options.center = center;
29737             this.gMapContext.circle = new google.maps.Circle(options);
29738             return this.gMapContext.circle;
29739         }
29740         
29741         return null;
29742     },
29743     
29744     setPosition: function(location) 
29745     {
29746         this.gMapContext.location = location;
29747         this.gMapContext.marker.setPosition(location);
29748         this.gMapContext.map.panTo(location);
29749         this.drawCircle(location, this.gMapContext.radius, {});
29750         
29751         var _this = this;
29752         
29753         if (this.gMapContext.settings.enableReverseGeocode) {
29754             this.gMapContext.geodecoder.geocode({
29755                 latLng: this.gMapContext.location
29756             }, function(results, status) {
29757                 
29758                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29759                     _this.gMapContext.locationName = results[0].formatted_address;
29760                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29761                     
29762                     _this.fireEvent('positionchanged', this, location);
29763                 }
29764             });
29765             
29766             return;
29767         }
29768         
29769         this.fireEvent('positionchanged', this, location);
29770     },
29771     
29772     resize: function()
29773     {
29774         google.maps.event.trigger(this.gMapContext.map, "resize");
29775         
29776         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29777         
29778         this.fireEvent('resize', this);
29779     },
29780     
29781     setPositionByLatLng: function(latitude, longitude)
29782     {
29783         this.setPosition(new google.maps.LatLng(latitude, longitude));
29784     },
29785     
29786     getCurrentPosition: function() 
29787     {
29788         return {
29789             latitude: this.gMapContext.location.lat(),
29790             longitude: this.gMapContext.location.lng()
29791         };
29792     },
29793     
29794     getAddressName: function() 
29795     {
29796         return this.gMapContext.locationName;
29797     },
29798     
29799     getAddressComponents: function() 
29800     {
29801         return this.gMapContext.addressComponents;
29802     },
29803     
29804     address_component_from_google_geocode: function(address_components) 
29805     {
29806         var result = {};
29807         
29808         for (var i = 0; i < address_components.length; i++) {
29809             var component = address_components[i];
29810             if (component.types.indexOf("postal_code") >= 0) {
29811                 result.postalCode = component.short_name;
29812             } else if (component.types.indexOf("street_number") >= 0) {
29813                 result.streetNumber = component.short_name;
29814             } else if (component.types.indexOf("route") >= 0) {
29815                 result.streetName = component.short_name;
29816             } else if (component.types.indexOf("neighborhood") >= 0) {
29817                 result.city = component.short_name;
29818             } else if (component.types.indexOf("locality") >= 0) {
29819                 result.city = component.short_name;
29820             } else if (component.types.indexOf("sublocality") >= 0) {
29821                 result.district = component.short_name;
29822             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29823                 result.stateOrProvince = component.short_name;
29824             } else if (component.types.indexOf("country") >= 0) {
29825                 result.country = component.short_name;
29826             }
29827         }
29828         
29829         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29830         result.addressLine2 = "";
29831         return result;
29832     },
29833     
29834     setZoomLevel: function(zoom)
29835     {
29836         this.gMapContext.map.setZoom(zoom);
29837     },
29838     
29839     show: function()
29840     {
29841         if(!this.el){
29842             return;
29843         }
29844         
29845         this.el.show();
29846         
29847         this.resize();
29848         
29849         this.fireEvent('show', this);
29850     },
29851     
29852     hide: function()
29853     {
29854         if(!this.el){
29855             return;
29856         }
29857         
29858         this.el.hide();
29859         
29860         this.fireEvent('hide', this);
29861     }
29862     
29863 });
29864
29865 Roo.apply(Roo.bootstrap.LocationPicker, {
29866     
29867     OverlayView : function(map, options)
29868     {
29869         options = options || {};
29870         
29871         this.setMap(map);
29872     }
29873     
29874     
29875 });/**
29876  * @class Roo.bootstrap.Alert
29877  * @extends Roo.bootstrap.Component
29878  * Bootstrap Alert class - shows an alert area box
29879  * eg
29880  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29881   Enter a valid email address
29882 </div>
29883  * @licence LGPL
29884  * @cfg {String} title The title of alert
29885  * @cfg {String} html The content of alert
29886  * @cfg {String} weight (  success | info | warning | danger )
29887  * @cfg {String} fa font-awesomeicon
29888  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29889  * @cfg {Boolean} close true to show a x closer
29890  * 
29891  * 
29892  * @constructor
29893  * Create a new alert
29894  * @param {Object} config The config object
29895  */
29896
29897
29898 Roo.bootstrap.Alert = function(config){
29899     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29900     
29901 };
29902
29903 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29904     
29905     title: '',
29906     html: '',
29907     weight: false,
29908     fa: false,
29909     faicon: false, // BC
29910     close : false,
29911     
29912     
29913     getAutoCreate : function()
29914     {
29915         
29916         var cfg = {
29917             tag : 'div',
29918             cls : 'alert',
29919             cn : [
29920                 {
29921                     tag: 'button',
29922                     type :  "button",
29923                     cls: "close",
29924                     html : '×',
29925                     style : this.close ? '' : 'display:none'
29926                 },
29927                 {
29928                     tag : 'i',
29929                     cls : 'roo-alert-icon'
29930                     
29931                 },
29932                 {
29933                     tag : 'b',
29934                     cls : 'roo-alert-title',
29935                     html : this.title
29936                 },
29937                 {
29938                     tag : 'span',
29939                     cls : 'roo-alert-text',
29940                     html : this.html
29941                 }
29942             ]
29943         };
29944         
29945         if(this.faicon){
29946             cfg.cn[0].cls += ' fa ' + this.faicon;
29947         }
29948         if(this.fa){
29949             cfg.cn[0].cls += ' fa ' + this.fa;
29950         }
29951         
29952         if(this.weight){
29953             cfg.cls += ' alert-' + this.weight;
29954         }
29955         
29956         return cfg;
29957     },
29958     
29959     initEvents: function() 
29960     {
29961         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29962         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29963         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29964         if (this.seconds > 0) {
29965             this.hide.defer(this.seconds, this);
29966         }
29967     },
29968     
29969     setTitle : function(str)
29970     {
29971         this.titleEl.dom.innerHTML = str;
29972     },
29973     
29974     setText : function(str)
29975     {
29976         this.titleEl.dom.innerHTML = str;
29977     },
29978     
29979     setWeight : function(weight)
29980     {
29981         if(this.weight){
29982             this.el.removeClass('alert-' + this.weight);
29983         }
29984         
29985         this.weight = weight;
29986         
29987         this.el.addClass('alert-' + this.weight);
29988     },
29989     
29990     setIcon : function(icon)
29991     {
29992         if(this.faicon){
29993             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29994         }
29995         
29996         this.faicon = icon;
29997         
29998         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29999     },
30000     
30001     hide: function() 
30002     {
30003         this.el.hide();   
30004     },
30005     
30006     show: function() 
30007     {  
30008         this.el.show();   
30009     }
30010     
30011 });
30012
30013  
30014 /*
30015 * Licence: LGPL
30016 */
30017
30018 /**
30019  * @class Roo.bootstrap.UploadCropbox
30020  * @extends Roo.bootstrap.Component
30021  * Bootstrap UploadCropbox class
30022  * @cfg {String} emptyText show when image has been loaded
30023  * @cfg {String} rotateNotify show when image too small to rotate
30024  * @cfg {Number} errorTimeout default 3000
30025  * @cfg {Number} minWidth default 300
30026  * @cfg {Number} minHeight default 300
30027  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30028  * @cfg {Boolean} isDocument (true|false) default false
30029  * @cfg {String} url action url
30030  * @cfg {String} paramName default 'imageUpload'
30031  * @cfg {String} method default POST
30032  * @cfg {Boolean} loadMask (true|false) default true
30033  * @cfg {Boolean} loadingText default 'Loading...'
30034  * 
30035  * @constructor
30036  * Create a new UploadCropbox
30037  * @param {Object} config The config object
30038  */
30039
30040 Roo.bootstrap.UploadCropbox = function(config){
30041     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30042     
30043     this.addEvents({
30044         /**
30045          * @event beforeselectfile
30046          * Fire before select file
30047          * @param {Roo.bootstrap.UploadCropbox} this
30048          */
30049         "beforeselectfile" : true,
30050         /**
30051          * @event initial
30052          * Fire after initEvent
30053          * @param {Roo.bootstrap.UploadCropbox} this
30054          */
30055         "initial" : true,
30056         /**
30057          * @event crop
30058          * Fire after initEvent
30059          * @param {Roo.bootstrap.UploadCropbox} this
30060          * @param {String} data
30061          */
30062         "crop" : true,
30063         /**
30064          * @event prepare
30065          * Fire when preparing the file data
30066          * @param {Roo.bootstrap.UploadCropbox} this
30067          * @param {Object} file
30068          */
30069         "prepare" : true,
30070         /**
30071          * @event exception
30072          * Fire when get exception
30073          * @param {Roo.bootstrap.UploadCropbox} this
30074          * @param {XMLHttpRequest} xhr
30075          */
30076         "exception" : true,
30077         /**
30078          * @event beforeloadcanvas
30079          * Fire before load the canvas
30080          * @param {Roo.bootstrap.UploadCropbox} this
30081          * @param {String} src
30082          */
30083         "beforeloadcanvas" : true,
30084         /**
30085          * @event trash
30086          * Fire when trash image
30087          * @param {Roo.bootstrap.UploadCropbox} this
30088          */
30089         "trash" : true,
30090         /**
30091          * @event download
30092          * Fire when download the image
30093          * @param {Roo.bootstrap.UploadCropbox} this
30094          */
30095         "download" : true,
30096         /**
30097          * @event footerbuttonclick
30098          * Fire when footerbuttonclick
30099          * @param {Roo.bootstrap.UploadCropbox} this
30100          * @param {String} type
30101          */
30102         "footerbuttonclick" : true,
30103         /**
30104          * @event resize
30105          * Fire when resize
30106          * @param {Roo.bootstrap.UploadCropbox} this
30107          */
30108         "resize" : true,
30109         /**
30110          * @event rotate
30111          * Fire when rotate the image
30112          * @param {Roo.bootstrap.UploadCropbox} this
30113          * @param {String} pos
30114          */
30115         "rotate" : true,
30116         /**
30117          * @event inspect
30118          * Fire when inspect the file
30119          * @param {Roo.bootstrap.UploadCropbox} this
30120          * @param {Object} file
30121          */
30122         "inspect" : true,
30123         /**
30124          * @event upload
30125          * Fire when xhr upload the file
30126          * @param {Roo.bootstrap.UploadCropbox} this
30127          * @param {Object} data
30128          */
30129         "upload" : true,
30130         /**
30131          * @event arrange
30132          * Fire when arrange the file data
30133          * @param {Roo.bootstrap.UploadCropbox} this
30134          * @param {Object} formData
30135          */
30136         "arrange" : true
30137     });
30138     
30139     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30140 };
30141
30142 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30143     
30144     emptyText : 'Click to upload image',
30145     rotateNotify : 'Image is too small to rotate',
30146     errorTimeout : 3000,
30147     scale : 0,
30148     baseScale : 1,
30149     rotate : 0,
30150     dragable : false,
30151     pinching : false,
30152     mouseX : 0,
30153     mouseY : 0,
30154     cropData : false,
30155     minWidth : 300,
30156     minHeight : 300,
30157     file : false,
30158     exif : {},
30159     baseRotate : 1,
30160     cropType : 'image/jpeg',
30161     buttons : false,
30162     canvasLoaded : false,
30163     isDocument : false,
30164     method : 'POST',
30165     paramName : 'imageUpload',
30166     loadMask : true,
30167     loadingText : 'Loading...',
30168     maskEl : false,
30169     
30170     getAutoCreate : function()
30171     {
30172         var cfg = {
30173             tag : 'div',
30174             cls : 'roo-upload-cropbox',
30175             cn : [
30176                 {
30177                     tag : 'input',
30178                     cls : 'roo-upload-cropbox-selector',
30179                     type : 'file'
30180                 },
30181                 {
30182                     tag : 'div',
30183                     cls : 'roo-upload-cropbox-body',
30184                     style : 'cursor:pointer',
30185                     cn : [
30186                         {
30187                             tag : 'div',
30188                             cls : 'roo-upload-cropbox-preview'
30189                         },
30190                         {
30191                             tag : 'div',
30192                             cls : 'roo-upload-cropbox-thumb'
30193                         },
30194                         {
30195                             tag : 'div',
30196                             cls : 'roo-upload-cropbox-empty-notify',
30197                             html : this.emptyText
30198                         },
30199                         {
30200                             tag : 'div',
30201                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30202                             html : this.rotateNotify
30203                         }
30204                     ]
30205                 },
30206                 {
30207                     tag : 'div',
30208                     cls : 'roo-upload-cropbox-footer',
30209                     cn : {
30210                         tag : 'div',
30211                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30212                         cn : []
30213                     }
30214                 }
30215             ]
30216         };
30217         
30218         return cfg;
30219     },
30220     
30221     onRender : function(ct, position)
30222     {
30223         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30224         
30225         if (this.buttons.length) {
30226             
30227             Roo.each(this.buttons, function(bb) {
30228                 
30229                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30230                 
30231                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30232                 
30233             }, this);
30234         }
30235         
30236         if(this.loadMask){
30237             this.maskEl = this.el;
30238         }
30239     },
30240     
30241     initEvents : function()
30242     {
30243         this.urlAPI = (window.createObjectURL && window) || 
30244                                 (window.URL && URL.revokeObjectURL && URL) || 
30245                                 (window.webkitURL && webkitURL);
30246                         
30247         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30248         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30249         
30250         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30251         this.selectorEl.hide();
30252         
30253         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30254         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30255         
30256         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30257         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258         this.thumbEl.hide();
30259         
30260         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30261         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30262         
30263         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30264         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30265         this.errorEl.hide();
30266         
30267         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30268         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30269         this.footerEl.hide();
30270         
30271         this.setThumbBoxSize();
30272         
30273         this.bind();
30274         
30275         this.resize();
30276         
30277         this.fireEvent('initial', this);
30278     },
30279
30280     bind : function()
30281     {
30282         var _this = this;
30283         
30284         window.addEventListener("resize", function() { _this.resize(); } );
30285         
30286         this.bodyEl.on('click', this.beforeSelectFile, this);
30287         
30288         if(Roo.isTouch){
30289             this.bodyEl.on('touchstart', this.onTouchStart, this);
30290             this.bodyEl.on('touchmove', this.onTouchMove, this);
30291             this.bodyEl.on('touchend', this.onTouchEnd, this);
30292         }
30293         
30294         if(!Roo.isTouch){
30295             this.bodyEl.on('mousedown', this.onMouseDown, this);
30296             this.bodyEl.on('mousemove', this.onMouseMove, this);
30297             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30298             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30299             Roo.get(document).on('mouseup', this.onMouseUp, this);
30300         }
30301         
30302         this.selectorEl.on('change', this.onFileSelected, this);
30303     },
30304     
30305     reset : function()
30306     {    
30307         this.scale = 0;
30308         this.baseScale = 1;
30309         this.rotate = 0;
30310         this.baseRotate = 1;
30311         this.dragable = false;
30312         this.pinching = false;
30313         this.mouseX = 0;
30314         this.mouseY = 0;
30315         this.cropData = false;
30316         this.notifyEl.dom.innerHTML = this.emptyText;
30317         
30318         this.selectorEl.dom.value = '';
30319         
30320     },
30321     
30322     resize : function()
30323     {
30324         if(this.fireEvent('resize', this) != false){
30325             this.setThumbBoxPosition();
30326             this.setCanvasPosition();
30327         }
30328     },
30329     
30330     onFooterButtonClick : function(e, el, o, type)
30331     {
30332         switch (type) {
30333             case 'rotate-left' :
30334                 this.onRotateLeft(e);
30335                 break;
30336             case 'rotate-right' :
30337                 this.onRotateRight(e);
30338                 break;
30339             case 'picture' :
30340                 this.beforeSelectFile(e);
30341                 break;
30342             case 'trash' :
30343                 this.trash(e);
30344                 break;
30345             case 'crop' :
30346                 this.crop(e);
30347                 break;
30348             case 'download' :
30349                 this.download(e);
30350                 break;
30351             default :
30352                 break;
30353         }
30354         
30355         this.fireEvent('footerbuttonclick', this, type);
30356     },
30357     
30358     beforeSelectFile : function(e)
30359     {
30360         e.preventDefault();
30361         
30362         if(this.fireEvent('beforeselectfile', this) != false){
30363             this.selectorEl.dom.click();
30364         }
30365     },
30366     
30367     onFileSelected : function(e)
30368     {
30369         e.preventDefault();
30370         
30371         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30372             return;
30373         }
30374         
30375         var file = this.selectorEl.dom.files[0];
30376         
30377         if(this.fireEvent('inspect', this, file) != false){
30378             this.prepare(file);
30379         }
30380         
30381     },
30382     
30383     trash : function(e)
30384     {
30385         this.fireEvent('trash', this);
30386     },
30387     
30388     download : function(e)
30389     {
30390         this.fireEvent('download', this);
30391     },
30392     
30393     loadCanvas : function(src)
30394     {   
30395         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30396             
30397             this.reset();
30398             
30399             this.imageEl = document.createElement('img');
30400             
30401             var _this = this;
30402             
30403             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30404             
30405             this.imageEl.src = src;
30406         }
30407     },
30408     
30409     onLoadCanvas : function()
30410     {   
30411         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30412         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30413         
30414         this.bodyEl.un('click', this.beforeSelectFile, this);
30415         
30416         this.notifyEl.hide();
30417         this.thumbEl.show();
30418         this.footerEl.show();
30419         
30420         this.baseRotateLevel();
30421         
30422         if(this.isDocument){
30423             this.setThumbBoxSize();
30424         }
30425         
30426         this.setThumbBoxPosition();
30427         
30428         this.baseScaleLevel();
30429         
30430         this.draw();
30431         
30432         this.resize();
30433         
30434         this.canvasLoaded = true;
30435         
30436         if(this.loadMask){
30437             this.maskEl.unmask();
30438         }
30439         
30440     },
30441     
30442     setCanvasPosition : function()
30443     {   
30444         if(!this.canvasEl){
30445             return;
30446         }
30447         
30448         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30449         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30450         
30451         this.previewEl.setLeft(pw);
30452         this.previewEl.setTop(ph);
30453         
30454     },
30455     
30456     onMouseDown : function(e)
30457     {   
30458         e.stopEvent();
30459         
30460         this.dragable = true;
30461         this.pinching = false;
30462         
30463         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30464             this.dragable = false;
30465             return;
30466         }
30467         
30468         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30469         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30470         
30471     },
30472     
30473     onMouseMove : function(e)
30474     {   
30475         e.stopEvent();
30476         
30477         if(!this.canvasLoaded){
30478             return;
30479         }
30480         
30481         if (!this.dragable){
30482             return;
30483         }
30484         
30485         var minX = Math.ceil(this.thumbEl.getLeft(true));
30486         var minY = Math.ceil(this.thumbEl.getTop(true));
30487         
30488         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30489         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30490         
30491         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30492         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30493         
30494         x = x - this.mouseX;
30495         y = y - this.mouseY;
30496         
30497         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30498         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30499         
30500         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30501         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30502         
30503         this.previewEl.setLeft(bgX);
30504         this.previewEl.setTop(bgY);
30505         
30506         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30507         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30508     },
30509     
30510     onMouseUp : function(e)
30511     {   
30512         e.stopEvent();
30513         
30514         this.dragable = false;
30515     },
30516     
30517     onMouseWheel : function(e)
30518     {   
30519         e.stopEvent();
30520         
30521         this.startScale = this.scale;
30522         
30523         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30524         
30525         if(!this.zoomable()){
30526             this.scale = this.startScale;
30527             return;
30528         }
30529         
30530         this.draw();
30531         
30532         return;
30533     },
30534     
30535     zoomable : function()
30536     {
30537         var minScale = this.thumbEl.getWidth() / this.minWidth;
30538         
30539         if(this.minWidth < this.minHeight){
30540             minScale = this.thumbEl.getHeight() / this.minHeight;
30541         }
30542         
30543         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30544         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30545         
30546         if(
30547                 this.isDocument &&
30548                 (this.rotate == 0 || this.rotate == 180) && 
30549                 (
30550                     width > this.imageEl.OriginWidth || 
30551                     height > this.imageEl.OriginHeight ||
30552                     (width < this.minWidth && height < this.minHeight)
30553                 )
30554         ){
30555             return false;
30556         }
30557         
30558         if(
30559                 this.isDocument &&
30560                 (this.rotate == 90 || this.rotate == 270) && 
30561                 (
30562                     width > this.imageEl.OriginWidth || 
30563                     height > this.imageEl.OriginHeight ||
30564                     (width < this.minHeight && height < this.minWidth)
30565                 )
30566         ){
30567             return false;
30568         }
30569         
30570         if(
30571                 !this.isDocument &&
30572                 (this.rotate == 0 || this.rotate == 180) && 
30573                 (
30574                     width < this.minWidth || 
30575                     width > this.imageEl.OriginWidth || 
30576                     height < this.minHeight || 
30577                     height > this.imageEl.OriginHeight
30578                 )
30579         ){
30580             return false;
30581         }
30582         
30583         if(
30584                 !this.isDocument &&
30585                 (this.rotate == 90 || this.rotate == 270) && 
30586                 (
30587                     width < this.minHeight || 
30588                     width > this.imageEl.OriginWidth || 
30589                     height < this.minWidth || 
30590                     height > this.imageEl.OriginHeight
30591                 )
30592         ){
30593             return false;
30594         }
30595         
30596         return true;
30597         
30598     },
30599     
30600     onRotateLeft : function(e)
30601     {   
30602         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30603             
30604             var minScale = this.thumbEl.getWidth() / this.minWidth;
30605             
30606             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30607             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30608             
30609             this.startScale = this.scale;
30610             
30611             while (this.getScaleLevel() < minScale){
30612             
30613                 this.scale = this.scale + 1;
30614                 
30615                 if(!this.zoomable()){
30616                     break;
30617                 }
30618                 
30619                 if(
30620                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30621                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30622                 ){
30623                     continue;
30624                 }
30625                 
30626                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30627
30628                 this.draw();
30629                 
30630                 return;
30631             }
30632             
30633             this.scale = this.startScale;
30634             
30635             this.onRotateFail();
30636             
30637             return false;
30638         }
30639         
30640         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30641
30642         if(this.isDocument){
30643             this.setThumbBoxSize();
30644             this.setThumbBoxPosition();
30645             this.setCanvasPosition();
30646         }
30647         
30648         this.draw();
30649         
30650         this.fireEvent('rotate', this, 'left');
30651         
30652     },
30653     
30654     onRotateRight : function(e)
30655     {
30656         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30657             
30658             var minScale = this.thumbEl.getWidth() / this.minWidth;
30659         
30660             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30661             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30662             
30663             this.startScale = this.scale;
30664             
30665             while (this.getScaleLevel() < minScale){
30666             
30667                 this.scale = this.scale + 1;
30668                 
30669                 if(!this.zoomable()){
30670                     break;
30671                 }
30672                 
30673                 if(
30674                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30675                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30676                 ){
30677                     continue;
30678                 }
30679                 
30680                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30681
30682                 this.draw();
30683                 
30684                 return;
30685             }
30686             
30687             this.scale = this.startScale;
30688             
30689             this.onRotateFail();
30690             
30691             return false;
30692         }
30693         
30694         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30695
30696         if(this.isDocument){
30697             this.setThumbBoxSize();
30698             this.setThumbBoxPosition();
30699             this.setCanvasPosition();
30700         }
30701         
30702         this.draw();
30703         
30704         this.fireEvent('rotate', this, 'right');
30705     },
30706     
30707     onRotateFail : function()
30708     {
30709         this.errorEl.show(true);
30710         
30711         var _this = this;
30712         
30713         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30714     },
30715     
30716     draw : function()
30717     {
30718         this.previewEl.dom.innerHTML = '';
30719         
30720         var canvasEl = document.createElement("canvas");
30721         
30722         var contextEl = canvasEl.getContext("2d");
30723         
30724         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30725         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30726         var center = this.imageEl.OriginWidth / 2;
30727         
30728         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30729             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30730             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30731             center = this.imageEl.OriginHeight / 2;
30732         }
30733         
30734         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30735         
30736         contextEl.translate(center, center);
30737         contextEl.rotate(this.rotate * Math.PI / 180);
30738
30739         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30740         
30741         this.canvasEl = document.createElement("canvas");
30742         
30743         this.contextEl = this.canvasEl.getContext("2d");
30744         
30745         switch (this.rotate) {
30746             case 0 :
30747                 
30748                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30749                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30750                 
30751                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30752                 
30753                 break;
30754             case 90 : 
30755                 
30756                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30758                 
30759                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30760                     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);
30761                     break;
30762                 }
30763                 
30764                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30765                 
30766                 break;
30767             case 180 :
30768                 
30769                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30770                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30771                 
30772                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30773                     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);
30774                     break;
30775                 }
30776                 
30777                 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);
30778                 
30779                 break;
30780             case 270 :
30781                 
30782                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30783                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30784         
30785                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30786                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30787                     break;
30788                 }
30789                 
30790                 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);
30791                 
30792                 break;
30793             default : 
30794                 break;
30795         }
30796         
30797         this.previewEl.appendChild(this.canvasEl);
30798         
30799         this.setCanvasPosition();
30800     },
30801     
30802     crop : function()
30803     {
30804         if(!this.canvasLoaded){
30805             return;
30806         }
30807         
30808         var imageCanvas = document.createElement("canvas");
30809         
30810         var imageContext = imageCanvas.getContext("2d");
30811         
30812         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30813         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30814         
30815         var center = imageCanvas.width / 2;
30816         
30817         imageContext.translate(center, center);
30818         
30819         imageContext.rotate(this.rotate * Math.PI / 180);
30820         
30821         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30822         
30823         var canvas = document.createElement("canvas");
30824         
30825         var context = canvas.getContext("2d");
30826                 
30827         canvas.width = this.minWidth;
30828         canvas.height = this.minHeight;
30829
30830         switch (this.rotate) {
30831             case 0 :
30832                 
30833                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30834                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30835                 
30836                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30837                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30838                 
30839                 var targetWidth = this.minWidth - 2 * x;
30840                 var targetHeight = this.minHeight - 2 * y;
30841                 
30842                 var scale = 1;
30843                 
30844                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30845                     scale = targetWidth / width;
30846                 }
30847                 
30848                 if(x > 0 && y == 0){
30849                     scale = targetHeight / height;
30850                 }
30851                 
30852                 if(x > 0 && y > 0){
30853                     scale = targetWidth / width;
30854                     
30855                     if(width < height){
30856                         scale = targetHeight / height;
30857                     }
30858                 }
30859                 
30860                 context.scale(scale, scale);
30861                 
30862                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30863                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30864
30865                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30866                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30867
30868                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30869                 
30870                 break;
30871             case 90 : 
30872                 
30873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30875                 
30876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30878                 
30879                 var targetWidth = this.minWidth - 2 * x;
30880                 var targetHeight = this.minHeight - 2 * y;
30881                 
30882                 var scale = 1;
30883                 
30884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30885                     scale = targetWidth / width;
30886                 }
30887                 
30888                 if(x > 0 && y == 0){
30889                     scale = targetHeight / height;
30890                 }
30891                 
30892                 if(x > 0 && y > 0){
30893                     scale = targetWidth / width;
30894                     
30895                     if(width < height){
30896                         scale = targetHeight / height;
30897                     }
30898                 }
30899                 
30900                 context.scale(scale, scale);
30901                 
30902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30904
30905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30907                 
30908                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30909                 
30910                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30911                 
30912                 break;
30913             case 180 :
30914                 
30915                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30916                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30917                 
30918                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30919                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30920                 
30921                 var targetWidth = this.minWidth - 2 * x;
30922                 var targetHeight = this.minHeight - 2 * y;
30923                 
30924                 var scale = 1;
30925                 
30926                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30927                     scale = targetWidth / width;
30928                 }
30929                 
30930                 if(x > 0 && y == 0){
30931                     scale = targetHeight / height;
30932                 }
30933                 
30934                 if(x > 0 && y > 0){
30935                     scale = targetWidth / width;
30936                     
30937                     if(width < height){
30938                         scale = targetHeight / height;
30939                     }
30940                 }
30941                 
30942                 context.scale(scale, scale);
30943                 
30944                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30945                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30946
30947                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30948                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30949
30950                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30951                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30952                 
30953                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30954                 
30955                 break;
30956             case 270 :
30957                 
30958                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30959                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30960                 
30961                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30962                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30963                 
30964                 var targetWidth = this.minWidth - 2 * x;
30965                 var targetHeight = this.minHeight - 2 * y;
30966                 
30967                 var scale = 1;
30968                 
30969                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30970                     scale = targetWidth / width;
30971                 }
30972                 
30973                 if(x > 0 && y == 0){
30974                     scale = targetHeight / height;
30975                 }
30976                 
30977                 if(x > 0 && y > 0){
30978                     scale = targetWidth / width;
30979                     
30980                     if(width < height){
30981                         scale = targetHeight / height;
30982                     }
30983                 }
30984                 
30985                 context.scale(scale, scale);
30986                 
30987                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30988                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30989
30990                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30991                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30992                 
30993                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30994                 
30995                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30996                 
30997                 break;
30998             default : 
30999                 break;
31000         }
31001         
31002         this.cropData = canvas.toDataURL(this.cropType);
31003         
31004         if(this.fireEvent('crop', this, this.cropData) !== false){
31005             this.process(this.file, this.cropData);
31006         }
31007         
31008         return;
31009         
31010     },
31011     
31012     setThumbBoxSize : function()
31013     {
31014         var width, height;
31015         
31016         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31017             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31018             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31019             
31020             this.minWidth = width;
31021             this.minHeight = height;
31022             
31023             if(this.rotate == 90 || this.rotate == 270){
31024                 this.minWidth = height;
31025                 this.minHeight = width;
31026             }
31027         }
31028         
31029         height = 300;
31030         width = Math.ceil(this.minWidth * height / this.minHeight);
31031         
31032         if(this.minWidth > this.minHeight){
31033             width = 300;
31034             height = Math.ceil(this.minHeight * width / this.minWidth);
31035         }
31036         
31037         this.thumbEl.setStyle({
31038             width : width + 'px',
31039             height : height + 'px'
31040         });
31041
31042         return;
31043             
31044     },
31045     
31046     setThumbBoxPosition : function()
31047     {
31048         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31049         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31050         
31051         this.thumbEl.setLeft(x);
31052         this.thumbEl.setTop(y);
31053         
31054     },
31055     
31056     baseRotateLevel : function()
31057     {
31058         this.baseRotate = 1;
31059         
31060         if(
31061                 typeof(this.exif) != 'undefined' &&
31062                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31063                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31064         ){
31065             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31066         }
31067         
31068         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31069         
31070     },
31071     
31072     baseScaleLevel : function()
31073     {
31074         var width, height;
31075         
31076         if(this.isDocument){
31077             
31078             if(this.baseRotate == 6 || this.baseRotate == 8){
31079             
31080                 height = this.thumbEl.getHeight();
31081                 this.baseScale = height / this.imageEl.OriginWidth;
31082
31083                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31084                     width = this.thumbEl.getWidth();
31085                     this.baseScale = width / this.imageEl.OriginHeight;
31086                 }
31087
31088                 return;
31089             }
31090
31091             height = this.thumbEl.getHeight();
31092             this.baseScale = height / this.imageEl.OriginHeight;
31093
31094             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31095                 width = this.thumbEl.getWidth();
31096                 this.baseScale = width / this.imageEl.OriginWidth;
31097             }
31098
31099             return;
31100         }
31101         
31102         if(this.baseRotate == 6 || this.baseRotate == 8){
31103             
31104             width = this.thumbEl.getHeight();
31105             this.baseScale = width / this.imageEl.OriginHeight;
31106             
31107             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31108                 height = this.thumbEl.getWidth();
31109                 this.baseScale = height / this.imageEl.OriginHeight;
31110             }
31111             
31112             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31113                 height = this.thumbEl.getWidth();
31114                 this.baseScale = height / this.imageEl.OriginHeight;
31115                 
31116                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31117                     width = this.thumbEl.getHeight();
31118                     this.baseScale = width / this.imageEl.OriginWidth;
31119                 }
31120             }
31121             
31122             return;
31123         }
31124         
31125         width = this.thumbEl.getWidth();
31126         this.baseScale = width / this.imageEl.OriginWidth;
31127         
31128         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31129             height = this.thumbEl.getHeight();
31130             this.baseScale = height / this.imageEl.OriginHeight;
31131         }
31132         
31133         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31134             
31135             height = this.thumbEl.getHeight();
31136             this.baseScale = height / this.imageEl.OriginHeight;
31137             
31138             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31139                 width = this.thumbEl.getWidth();
31140                 this.baseScale = width / this.imageEl.OriginWidth;
31141             }
31142             
31143         }
31144         
31145         return;
31146     },
31147     
31148     getScaleLevel : function()
31149     {
31150         return this.baseScale * Math.pow(1.1, this.scale);
31151     },
31152     
31153     onTouchStart : function(e)
31154     {
31155         if(!this.canvasLoaded){
31156             this.beforeSelectFile(e);
31157             return;
31158         }
31159         
31160         var touches = e.browserEvent.touches;
31161         
31162         if(!touches){
31163             return;
31164         }
31165         
31166         if(touches.length == 1){
31167             this.onMouseDown(e);
31168             return;
31169         }
31170         
31171         if(touches.length != 2){
31172             return;
31173         }
31174         
31175         var coords = [];
31176         
31177         for(var i = 0, finger; finger = touches[i]; i++){
31178             coords.push(finger.pageX, finger.pageY);
31179         }
31180         
31181         var x = Math.pow(coords[0] - coords[2], 2);
31182         var y = Math.pow(coords[1] - coords[3], 2);
31183         
31184         this.startDistance = Math.sqrt(x + y);
31185         
31186         this.startScale = this.scale;
31187         
31188         this.pinching = true;
31189         this.dragable = false;
31190         
31191     },
31192     
31193     onTouchMove : function(e)
31194     {
31195         if(!this.pinching && !this.dragable){
31196             return;
31197         }
31198         
31199         var touches = e.browserEvent.touches;
31200         
31201         if(!touches){
31202             return;
31203         }
31204         
31205         if(this.dragable){
31206             this.onMouseMove(e);
31207             return;
31208         }
31209         
31210         var coords = [];
31211         
31212         for(var i = 0, finger; finger = touches[i]; i++){
31213             coords.push(finger.pageX, finger.pageY);
31214         }
31215         
31216         var x = Math.pow(coords[0] - coords[2], 2);
31217         var y = Math.pow(coords[1] - coords[3], 2);
31218         
31219         this.endDistance = Math.sqrt(x + y);
31220         
31221         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31222         
31223         if(!this.zoomable()){
31224             this.scale = this.startScale;
31225             return;
31226         }
31227         
31228         this.draw();
31229         
31230     },
31231     
31232     onTouchEnd : function(e)
31233     {
31234         this.pinching = false;
31235         this.dragable = false;
31236         
31237     },
31238     
31239     process : function(file, crop)
31240     {
31241         if(this.loadMask){
31242             this.maskEl.mask(this.loadingText);
31243         }
31244         
31245         this.xhr = new XMLHttpRequest();
31246         
31247         file.xhr = this.xhr;
31248
31249         this.xhr.open(this.method, this.url, true);
31250         
31251         var headers = {
31252             "Accept": "application/json",
31253             "Cache-Control": "no-cache",
31254             "X-Requested-With": "XMLHttpRequest"
31255         };
31256         
31257         for (var headerName in headers) {
31258             var headerValue = headers[headerName];
31259             if (headerValue) {
31260                 this.xhr.setRequestHeader(headerName, headerValue);
31261             }
31262         }
31263         
31264         var _this = this;
31265         
31266         this.xhr.onload = function()
31267         {
31268             _this.xhrOnLoad(_this.xhr);
31269         }
31270         
31271         this.xhr.onerror = function()
31272         {
31273             _this.xhrOnError(_this.xhr);
31274         }
31275         
31276         var formData = new FormData();
31277
31278         formData.append('returnHTML', 'NO');
31279         
31280         if(crop){
31281             formData.append('crop', crop);
31282         }
31283         
31284         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31285             formData.append(this.paramName, file, file.name);
31286         }
31287         
31288         if(typeof(file.filename) != 'undefined'){
31289             formData.append('filename', file.filename);
31290         }
31291         
31292         if(typeof(file.mimetype) != 'undefined'){
31293             formData.append('mimetype', file.mimetype);
31294         }
31295         
31296         if(this.fireEvent('arrange', this, formData) != false){
31297             this.xhr.send(formData);
31298         };
31299     },
31300     
31301     xhrOnLoad : function(xhr)
31302     {
31303         if(this.loadMask){
31304             this.maskEl.unmask();
31305         }
31306         
31307         if (xhr.readyState !== 4) {
31308             this.fireEvent('exception', this, xhr);
31309             return;
31310         }
31311
31312         var response = Roo.decode(xhr.responseText);
31313         
31314         if(!response.success){
31315             this.fireEvent('exception', this, xhr);
31316             return;
31317         }
31318         
31319         var response = Roo.decode(xhr.responseText);
31320         
31321         this.fireEvent('upload', this, response);
31322         
31323     },
31324     
31325     xhrOnError : function()
31326     {
31327         if(this.loadMask){
31328             this.maskEl.unmask();
31329         }
31330         
31331         Roo.log('xhr on error');
31332         
31333         var response = Roo.decode(xhr.responseText);
31334           
31335         Roo.log(response);
31336         
31337     },
31338     
31339     prepare : function(file)
31340     {   
31341         if(this.loadMask){
31342             this.maskEl.mask(this.loadingText);
31343         }
31344         
31345         this.file = false;
31346         this.exif = {};
31347         
31348         if(typeof(file) === 'string'){
31349             this.loadCanvas(file);
31350             return;
31351         }
31352         
31353         if(!file || !this.urlAPI){
31354             return;
31355         }
31356         
31357         this.file = file;
31358         this.cropType = file.type;
31359         
31360         var _this = this;
31361         
31362         if(this.fireEvent('prepare', this, this.file) != false){
31363             
31364             var reader = new FileReader();
31365             
31366             reader.onload = function (e) {
31367                 if (e.target.error) {
31368                     Roo.log(e.target.error);
31369                     return;
31370                 }
31371                 
31372                 var buffer = e.target.result,
31373                     dataView = new DataView(buffer),
31374                     offset = 2,
31375                     maxOffset = dataView.byteLength - 4,
31376                     markerBytes,
31377                     markerLength;
31378                 
31379                 if (dataView.getUint16(0) === 0xffd8) {
31380                     while (offset < maxOffset) {
31381                         markerBytes = dataView.getUint16(offset);
31382                         
31383                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31384                             markerLength = dataView.getUint16(offset + 2) + 2;
31385                             if (offset + markerLength > dataView.byteLength) {
31386                                 Roo.log('Invalid meta data: Invalid segment size.');
31387                                 break;
31388                             }
31389                             
31390                             if(markerBytes == 0xffe1){
31391                                 _this.parseExifData(
31392                                     dataView,
31393                                     offset,
31394                                     markerLength
31395                                 );
31396                             }
31397                             
31398                             offset += markerLength;
31399                             
31400                             continue;
31401                         }
31402                         
31403                         break;
31404                     }
31405                     
31406                 }
31407                 
31408                 var url = _this.urlAPI.createObjectURL(_this.file);
31409                 
31410                 _this.loadCanvas(url);
31411                 
31412                 return;
31413             }
31414             
31415             reader.readAsArrayBuffer(this.file);
31416             
31417         }
31418         
31419     },
31420     
31421     parseExifData : function(dataView, offset, length)
31422     {
31423         var tiffOffset = offset + 10,
31424             littleEndian,
31425             dirOffset;
31426     
31427         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31428             // No Exif data, might be XMP data instead
31429             return;
31430         }
31431         
31432         // Check for the ASCII code for "Exif" (0x45786966):
31433         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31434             // No Exif data, might be XMP data instead
31435             return;
31436         }
31437         if (tiffOffset + 8 > dataView.byteLength) {
31438             Roo.log('Invalid Exif data: Invalid segment size.');
31439             return;
31440         }
31441         // Check for the two null bytes:
31442         if (dataView.getUint16(offset + 8) !== 0x0000) {
31443             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31444             return;
31445         }
31446         // Check the byte alignment:
31447         switch (dataView.getUint16(tiffOffset)) {
31448         case 0x4949:
31449             littleEndian = true;
31450             break;
31451         case 0x4D4D:
31452             littleEndian = false;
31453             break;
31454         default:
31455             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31456             return;
31457         }
31458         // Check for the TIFF tag marker (0x002A):
31459         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31460             Roo.log('Invalid Exif data: Missing TIFF marker.');
31461             return;
31462         }
31463         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31464         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31465         
31466         this.parseExifTags(
31467             dataView,
31468             tiffOffset,
31469             tiffOffset + dirOffset,
31470             littleEndian
31471         );
31472     },
31473     
31474     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31475     {
31476         var tagsNumber,
31477             dirEndOffset,
31478             i;
31479         if (dirOffset + 6 > dataView.byteLength) {
31480             Roo.log('Invalid Exif data: Invalid directory offset.');
31481             return;
31482         }
31483         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31484         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31485         if (dirEndOffset + 4 > dataView.byteLength) {
31486             Roo.log('Invalid Exif data: Invalid directory size.');
31487             return;
31488         }
31489         for (i = 0; i < tagsNumber; i += 1) {
31490             this.parseExifTag(
31491                 dataView,
31492                 tiffOffset,
31493                 dirOffset + 2 + 12 * i, // tag offset
31494                 littleEndian
31495             );
31496         }
31497         // Return the offset to the next directory:
31498         return dataView.getUint32(dirEndOffset, littleEndian);
31499     },
31500     
31501     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31502     {
31503         var tag = dataView.getUint16(offset, littleEndian);
31504         
31505         this.exif[tag] = this.getExifValue(
31506             dataView,
31507             tiffOffset,
31508             offset,
31509             dataView.getUint16(offset + 2, littleEndian), // tag type
31510             dataView.getUint32(offset + 4, littleEndian), // tag length
31511             littleEndian
31512         );
31513     },
31514     
31515     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31516     {
31517         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31518             tagSize,
31519             dataOffset,
31520             values,
31521             i,
31522             str,
31523             c;
31524     
31525         if (!tagType) {
31526             Roo.log('Invalid Exif data: Invalid tag type.');
31527             return;
31528         }
31529         
31530         tagSize = tagType.size * length;
31531         // Determine if the value is contained in the dataOffset bytes,
31532         // or if the value at the dataOffset is a pointer to the actual data:
31533         dataOffset = tagSize > 4 ?
31534                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31535         if (dataOffset + tagSize > dataView.byteLength) {
31536             Roo.log('Invalid Exif data: Invalid data offset.');
31537             return;
31538         }
31539         if (length === 1) {
31540             return tagType.getValue(dataView, dataOffset, littleEndian);
31541         }
31542         values = [];
31543         for (i = 0; i < length; i += 1) {
31544             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31545         }
31546         
31547         if (tagType.ascii) {
31548             str = '';
31549             // Concatenate the chars:
31550             for (i = 0; i < values.length; i += 1) {
31551                 c = values[i];
31552                 // Ignore the terminating NULL byte(s):
31553                 if (c === '\u0000') {
31554                     break;
31555                 }
31556                 str += c;
31557             }
31558             return str;
31559         }
31560         return values;
31561     }
31562     
31563 });
31564
31565 Roo.apply(Roo.bootstrap.UploadCropbox, {
31566     tags : {
31567         'Orientation': 0x0112
31568     },
31569     
31570     Orientation: {
31571             1: 0, //'top-left',
31572 //            2: 'top-right',
31573             3: 180, //'bottom-right',
31574 //            4: 'bottom-left',
31575 //            5: 'left-top',
31576             6: 90, //'right-top',
31577 //            7: 'right-bottom',
31578             8: 270 //'left-bottom'
31579     },
31580     
31581     exifTagTypes : {
31582         // byte, 8-bit unsigned int:
31583         1: {
31584             getValue: function (dataView, dataOffset) {
31585                 return dataView.getUint8(dataOffset);
31586             },
31587             size: 1
31588         },
31589         // ascii, 8-bit byte:
31590         2: {
31591             getValue: function (dataView, dataOffset) {
31592                 return String.fromCharCode(dataView.getUint8(dataOffset));
31593             },
31594             size: 1,
31595             ascii: true
31596         },
31597         // short, 16 bit int:
31598         3: {
31599             getValue: function (dataView, dataOffset, littleEndian) {
31600                 return dataView.getUint16(dataOffset, littleEndian);
31601             },
31602             size: 2
31603         },
31604         // long, 32 bit int:
31605         4: {
31606             getValue: function (dataView, dataOffset, littleEndian) {
31607                 return dataView.getUint32(dataOffset, littleEndian);
31608             },
31609             size: 4
31610         },
31611         // rational = two long values, first is numerator, second is denominator:
31612         5: {
31613             getValue: function (dataView, dataOffset, littleEndian) {
31614                 return dataView.getUint32(dataOffset, littleEndian) /
31615                     dataView.getUint32(dataOffset + 4, littleEndian);
31616             },
31617             size: 8
31618         },
31619         // slong, 32 bit signed int:
31620         9: {
31621             getValue: function (dataView, dataOffset, littleEndian) {
31622                 return dataView.getInt32(dataOffset, littleEndian);
31623             },
31624             size: 4
31625         },
31626         // srational, two slongs, first is numerator, second is denominator:
31627         10: {
31628             getValue: function (dataView, dataOffset, littleEndian) {
31629                 return dataView.getInt32(dataOffset, littleEndian) /
31630                     dataView.getInt32(dataOffset + 4, littleEndian);
31631             },
31632             size: 8
31633         }
31634     },
31635     
31636     footer : {
31637         STANDARD : [
31638             {
31639                 tag : 'div',
31640                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31641                 action : 'rotate-left',
31642                 cn : [
31643                     {
31644                         tag : 'button',
31645                         cls : 'btn btn-default',
31646                         html : '<i class="fa fa-undo"></i>'
31647                     }
31648                 ]
31649             },
31650             {
31651                 tag : 'div',
31652                 cls : 'btn-group roo-upload-cropbox-picture',
31653                 action : 'picture',
31654                 cn : [
31655                     {
31656                         tag : 'button',
31657                         cls : 'btn btn-default',
31658                         html : '<i class="fa fa-picture-o"></i>'
31659                     }
31660                 ]
31661             },
31662             {
31663                 tag : 'div',
31664                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31665                 action : 'rotate-right',
31666                 cn : [
31667                     {
31668                         tag : 'button',
31669                         cls : 'btn btn-default',
31670                         html : '<i class="fa fa-repeat"></i>'
31671                     }
31672                 ]
31673             }
31674         ],
31675         DOCUMENT : [
31676             {
31677                 tag : 'div',
31678                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31679                 action : 'rotate-left',
31680                 cn : [
31681                     {
31682                         tag : 'button',
31683                         cls : 'btn btn-default',
31684                         html : '<i class="fa fa-undo"></i>'
31685                     }
31686                 ]
31687             },
31688             {
31689                 tag : 'div',
31690                 cls : 'btn-group roo-upload-cropbox-download',
31691                 action : 'download',
31692                 cn : [
31693                     {
31694                         tag : 'button',
31695                         cls : 'btn btn-default',
31696                         html : '<i class="fa fa-download"></i>'
31697                     }
31698                 ]
31699             },
31700             {
31701                 tag : 'div',
31702                 cls : 'btn-group roo-upload-cropbox-crop',
31703                 action : 'crop',
31704                 cn : [
31705                     {
31706                         tag : 'button',
31707                         cls : 'btn btn-default',
31708                         html : '<i class="fa fa-crop"></i>'
31709                     }
31710                 ]
31711             },
31712             {
31713                 tag : 'div',
31714                 cls : 'btn-group roo-upload-cropbox-trash',
31715                 action : 'trash',
31716                 cn : [
31717                     {
31718                         tag : 'button',
31719                         cls : 'btn btn-default',
31720                         html : '<i class="fa fa-trash"></i>'
31721                     }
31722                 ]
31723             },
31724             {
31725                 tag : 'div',
31726                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31727                 action : 'rotate-right',
31728                 cn : [
31729                     {
31730                         tag : 'button',
31731                         cls : 'btn btn-default',
31732                         html : '<i class="fa fa-repeat"></i>'
31733                     }
31734                 ]
31735             }
31736         ],
31737         ROTATOR : [
31738             {
31739                 tag : 'div',
31740                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31741                 action : 'rotate-left',
31742                 cn : [
31743                     {
31744                         tag : 'button',
31745                         cls : 'btn btn-default',
31746                         html : '<i class="fa fa-undo"></i>'
31747                     }
31748                 ]
31749             },
31750             {
31751                 tag : 'div',
31752                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31753                 action : 'rotate-right',
31754                 cn : [
31755                     {
31756                         tag : 'button',
31757                         cls : 'btn btn-default',
31758                         html : '<i class="fa fa-repeat"></i>'
31759                     }
31760                 ]
31761             }
31762         ]
31763     }
31764 });
31765
31766 /*
31767 * Licence: LGPL
31768 */
31769
31770 /**
31771  * @class Roo.bootstrap.DocumentManager
31772  * @extends Roo.bootstrap.Component
31773  * Bootstrap DocumentManager class
31774  * @cfg {String} paramName default 'imageUpload'
31775  * @cfg {String} toolTipName default 'filename'
31776  * @cfg {String} method default POST
31777  * @cfg {String} url action url
31778  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31779  * @cfg {Boolean} multiple multiple upload default true
31780  * @cfg {Number} thumbSize default 300
31781  * @cfg {String} fieldLabel
31782  * @cfg {Number} labelWidth default 4
31783  * @cfg {String} labelAlign (left|top) default left
31784  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31785 * @cfg {Number} labellg set the width of label (1-12)
31786  * @cfg {Number} labelmd set the width of label (1-12)
31787  * @cfg {Number} labelsm set the width of label (1-12)
31788  * @cfg {Number} labelxs set the width of label (1-12)
31789  * 
31790  * @constructor
31791  * Create a new DocumentManager
31792  * @param {Object} config The config object
31793  */
31794
31795 Roo.bootstrap.DocumentManager = function(config){
31796     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31797     
31798     this.files = [];
31799     this.delegates = [];
31800     
31801     this.addEvents({
31802         /**
31803          * @event initial
31804          * Fire when initial the DocumentManager
31805          * @param {Roo.bootstrap.DocumentManager} this
31806          */
31807         "initial" : true,
31808         /**
31809          * @event inspect
31810          * inspect selected file
31811          * @param {Roo.bootstrap.DocumentManager} this
31812          * @param {File} file
31813          */
31814         "inspect" : true,
31815         /**
31816          * @event exception
31817          * Fire when xhr load exception
31818          * @param {Roo.bootstrap.DocumentManager} this
31819          * @param {XMLHttpRequest} xhr
31820          */
31821         "exception" : true,
31822         /**
31823          * @event afterupload
31824          * Fire when xhr load exception
31825          * @param {Roo.bootstrap.DocumentManager} this
31826          * @param {XMLHttpRequest} xhr
31827          */
31828         "afterupload" : true,
31829         /**
31830          * @event prepare
31831          * prepare the form data
31832          * @param {Roo.bootstrap.DocumentManager} this
31833          * @param {Object} formData
31834          */
31835         "prepare" : true,
31836         /**
31837          * @event remove
31838          * Fire when remove the file
31839          * @param {Roo.bootstrap.DocumentManager} this
31840          * @param {Object} file
31841          */
31842         "remove" : true,
31843         /**
31844          * @event refresh
31845          * Fire after refresh the file
31846          * @param {Roo.bootstrap.DocumentManager} this
31847          */
31848         "refresh" : true,
31849         /**
31850          * @event click
31851          * Fire after click the image
31852          * @param {Roo.bootstrap.DocumentManager} this
31853          * @param {Object} file
31854          */
31855         "click" : true,
31856         /**
31857          * @event edit
31858          * Fire when upload a image and editable set to true
31859          * @param {Roo.bootstrap.DocumentManager} this
31860          * @param {Object} file
31861          */
31862         "edit" : true,
31863         /**
31864          * @event beforeselectfile
31865          * Fire before select file
31866          * @param {Roo.bootstrap.DocumentManager} this
31867          */
31868         "beforeselectfile" : true,
31869         /**
31870          * @event process
31871          * Fire before process file
31872          * @param {Roo.bootstrap.DocumentManager} this
31873          * @param {Object} file
31874          */
31875         "process" : true,
31876         /**
31877          * @event previewrendered
31878          * Fire when preview rendered
31879          * @param {Roo.bootstrap.DocumentManager} this
31880          * @param {Object} file
31881          */
31882         "previewrendered" : true,
31883         /**
31884          */
31885         "previewResize" : true
31886         
31887     });
31888 };
31889
31890 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31891     
31892     boxes : 0,
31893     inputName : '',
31894     thumbSize : 300,
31895     multiple : true,
31896     files : false,
31897     method : 'POST',
31898     url : '',
31899     paramName : 'imageUpload',
31900     toolTipName : 'filename',
31901     fieldLabel : '',
31902     labelWidth : 4,
31903     labelAlign : 'left',
31904     editable : true,
31905     delegates : false,
31906     xhr : false, 
31907     
31908     labellg : 0,
31909     labelmd : 0,
31910     labelsm : 0,
31911     labelxs : 0,
31912     
31913     getAutoCreate : function()
31914     {   
31915         var managerWidget = {
31916             tag : 'div',
31917             cls : 'roo-document-manager',
31918             cn : [
31919                 {
31920                     tag : 'input',
31921                     cls : 'roo-document-manager-selector',
31922                     type : 'file'
31923                 },
31924                 {
31925                     tag : 'div',
31926                     cls : 'roo-document-manager-uploader',
31927                     cn : [
31928                         {
31929                             tag : 'div',
31930                             cls : 'roo-document-manager-upload-btn',
31931                             html : '<i class="fa fa-plus"></i>'
31932                         }
31933                     ]
31934                     
31935                 }
31936             ]
31937         };
31938         
31939         var content = [
31940             {
31941                 tag : 'div',
31942                 cls : 'column col-md-12',
31943                 cn : managerWidget
31944             }
31945         ];
31946         
31947         if(this.fieldLabel.length){
31948             
31949             content = [
31950                 {
31951                     tag : 'div',
31952                     cls : 'column col-md-12',
31953                     html : this.fieldLabel
31954                 },
31955                 {
31956                     tag : 'div',
31957                     cls : 'column col-md-12',
31958                     cn : managerWidget
31959                 }
31960             ];
31961
31962             if(this.labelAlign == 'left'){
31963                 content = [
31964                     {
31965                         tag : 'div',
31966                         cls : 'column',
31967                         html : this.fieldLabel
31968                     },
31969                     {
31970                         tag : 'div',
31971                         cls : 'column',
31972                         cn : managerWidget
31973                     }
31974                 ];
31975                 
31976                 if(this.labelWidth > 12){
31977                     content[0].style = "width: " + this.labelWidth + 'px';
31978                 }
31979
31980                 if(this.labelWidth < 13 && this.labelmd == 0){
31981                     this.labelmd = this.labelWidth;
31982                 }
31983
31984                 if(this.labellg > 0){
31985                     content[0].cls += ' col-lg-' + this.labellg;
31986                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31987                 }
31988
31989                 if(this.labelmd > 0){
31990                     content[0].cls += ' col-md-' + this.labelmd;
31991                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31992                 }
31993
31994                 if(this.labelsm > 0){
31995                     content[0].cls += ' col-sm-' + this.labelsm;
31996                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31997                 }
31998
31999                 if(this.labelxs > 0){
32000                     content[0].cls += ' col-xs-' + this.labelxs;
32001                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32002                 }
32003                 
32004             }
32005         }
32006         
32007         var cfg = {
32008             tag : 'div',
32009             cls : 'row clearfix',
32010             cn : content
32011         };
32012         
32013         return cfg;
32014         
32015     },
32016     
32017     initEvents : function()
32018     {
32019         this.managerEl = this.el.select('.roo-document-manager', true).first();
32020         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32021         
32022         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32023         this.selectorEl.hide();
32024         
32025         if(this.multiple){
32026             this.selectorEl.attr('multiple', 'multiple');
32027         }
32028         
32029         this.selectorEl.on('change', this.onFileSelected, this);
32030         
32031         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32032         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32033         
32034         this.uploader.on('click', this.onUploaderClick, this);
32035         
32036         this.renderProgressDialog();
32037         
32038         var _this = this;
32039         
32040         window.addEventListener("resize", function() { _this.refresh(); } );
32041         
32042         this.fireEvent('initial', this);
32043     },
32044     
32045     renderProgressDialog : function()
32046     {
32047         var _this = this;
32048         
32049         this.progressDialog = new Roo.bootstrap.Modal({
32050             cls : 'roo-document-manager-progress-dialog',
32051             allow_close : false,
32052             animate : false,
32053             title : '',
32054             buttons : [
32055                 {
32056                     name  :'cancel',
32057                     weight : 'danger',
32058                     html : 'Cancel'
32059                 }
32060             ], 
32061             listeners : { 
32062                 btnclick : function() {
32063                     _this.uploadCancel();
32064                     this.hide();
32065                 }
32066             }
32067         });
32068          
32069         this.progressDialog.render(Roo.get(document.body));
32070          
32071         this.progress = new Roo.bootstrap.Progress({
32072             cls : 'roo-document-manager-progress',
32073             active : true,
32074             striped : true
32075         });
32076         
32077         this.progress.render(this.progressDialog.getChildContainer());
32078         
32079         this.progressBar = new Roo.bootstrap.ProgressBar({
32080             cls : 'roo-document-manager-progress-bar',
32081             aria_valuenow : 0,
32082             aria_valuemin : 0,
32083             aria_valuemax : 12,
32084             panel : 'success'
32085         });
32086         
32087         this.progressBar.render(this.progress.getChildContainer());
32088     },
32089     
32090     onUploaderClick : function(e)
32091     {
32092         e.preventDefault();
32093      
32094         if(this.fireEvent('beforeselectfile', this) != false){
32095             this.selectorEl.dom.click();
32096         }
32097         
32098     },
32099     
32100     onFileSelected : function(e)
32101     {
32102         e.preventDefault();
32103         
32104         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32105             return;
32106         }
32107         
32108         Roo.each(this.selectorEl.dom.files, function(file){
32109             if(this.fireEvent('inspect', this, file) != false){
32110                 this.files.push(file);
32111             }
32112         }, this);
32113         
32114         this.queue();
32115         
32116     },
32117     
32118     queue : function()
32119     {
32120         this.selectorEl.dom.value = '';
32121         
32122         if(!this.files || !this.files.length){
32123             return;
32124         }
32125         
32126         if(this.boxes > 0 && this.files.length > this.boxes){
32127             this.files = this.files.slice(0, this.boxes);
32128         }
32129         
32130         this.uploader.show();
32131         
32132         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32133             this.uploader.hide();
32134         }
32135         
32136         var _this = this;
32137         
32138         var files = [];
32139         
32140         var docs = [];
32141         
32142         Roo.each(this.files, function(file){
32143             
32144             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32145                 var f = this.renderPreview(file);
32146                 files.push(f);
32147                 return;
32148             }
32149             
32150             if(file.type.indexOf('image') != -1){
32151                 this.delegates.push(
32152                     (function(){
32153                         _this.process(file);
32154                     }).createDelegate(this)
32155                 );
32156         
32157                 return;
32158             }
32159             
32160             docs.push(
32161                 (function(){
32162                     _this.process(file);
32163                 }).createDelegate(this)
32164             );
32165             
32166         }, this);
32167         
32168         this.files = files;
32169         
32170         this.delegates = this.delegates.concat(docs);
32171         
32172         if(!this.delegates.length){
32173             this.refresh();
32174             return;
32175         }
32176         
32177         this.progressBar.aria_valuemax = this.delegates.length;
32178         
32179         this.arrange();
32180         
32181         return;
32182     },
32183     
32184     arrange : function()
32185     {
32186         if(!this.delegates.length){
32187             this.progressDialog.hide();
32188             this.refresh();
32189             return;
32190         }
32191         
32192         var delegate = this.delegates.shift();
32193         
32194         this.progressDialog.show();
32195         
32196         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32197         
32198         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32199         
32200         delegate();
32201     },
32202     
32203     refresh : function()
32204     {
32205         this.uploader.show();
32206         
32207         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32208             this.uploader.hide();
32209         }
32210         
32211         Roo.isTouch ? this.closable(false) : this.closable(true);
32212         
32213         this.fireEvent('refresh', this);
32214     },
32215     
32216     onRemove : function(e, el, o)
32217     {
32218         e.preventDefault();
32219         
32220         this.fireEvent('remove', this, o);
32221         
32222     },
32223     
32224     remove : function(o)
32225     {
32226         var files = [];
32227         
32228         Roo.each(this.files, function(file){
32229             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32230                 files.push(file);
32231                 return;
32232             }
32233
32234             o.target.remove();
32235
32236         }, this);
32237         
32238         this.files = files;
32239         
32240         this.refresh();
32241     },
32242     
32243     clear : function()
32244     {
32245         Roo.each(this.files, function(file){
32246             if(!file.target){
32247                 return;
32248             }
32249             
32250             file.target.remove();
32251
32252         }, this);
32253         
32254         this.files = [];
32255         
32256         this.refresh();
32257     },
32258     
32259     onClick : function(e, el, o)
32260     {
32261         e.preventDefault();
32262         
32263         this.fireEvent('click', this, o);
32264         
32265     },
32266     
32267     closable : function(closable)
32268     {
32269         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32270             
32271             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32272             
32273             if(closable){
32274                 el.show();
32275                 return;
32276             }
32277             
32278             el.hide();
32279             
32280         }, this);
32281     },
32282     
32283     xhrOnLoad : function(xhr)
32284     {
32285         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32286             el.remove();
32287         }, this);
32288         
32289         if (xhr.readyState !== 4) {
32290             this.arrange();
32291             this.fireEvent('exception', this, xhr);
32292             return;
32293         }
32294
32295         var response = Roo.decode(xhr.responseText);
32296         
32297         if(!response.success){
32298             this.arrange();
32299             this.fireEvent('exception', this, xhr);
32300             return;
32301         }
32302         
32303         var file = this.renderPreview(response.data);
32304         
32305         this.files.push(file);
32306         
32307         this.arrange();
32308         
32309         this.fireEvent('afterupload', this, xhr);
32310         
32311     },
32312     
32313     xhrOnError : function(xhr)
32314     {
32315         Roo.log('xhr on error');
32316         
32317         var response = Roo.decode(xhr.responseText);
32318           
32319         Roo.log(response);
32320         
32321         this.arrange();
32322     },
32323     
32324     process : function(file)
32325     {
32326         if(this.fireEvent('process', this, file) !== false){
32327             if(this.editable && file.type.indexOf('image') != -1){
32328                 this.fireEvent('edit', this, file);
32329                 return;
32330             }
32331
32332             this.uploadStart(file, false);
32333
32334             return;
32335         }
32336         
32337     },
32338     
32339     uploadStart : function(file, crop)
32340     {
32341         this.xhr = new XMLHttpRequest();
32342         
32343         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32344             this.arrange();
32345             return;
32346         }
32347         
32348         file.xhr = this.xhr;
32349             
32350         this.managerEl.createChild({
32351             tag : 'div',
32352             cls : 'roo-document-manager-loading',
32353             cn : [
32354                 {
32355                     tag : 'div',
32356                     tooltip : file.name,
32357                     cls : 'roo-document-manager-thumb',
32358                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32359                 }
32360             ]
32361
32362         });
32363
32364         this.xhr.open(this.method, this.url, true);
32365         
32366         var headers = {
32367             "Accept": "application/json",
32368             "Cache-Control": "no-cache",
32369             "X-Requested-With": "XMLHttpRequest"
32370         };
32371         
32372         for (var headerName in headers) {
32373             var headerValue = headers[headerName];
32374             if (headerValue) {
32375                 this.xhr.setRequestHeader(headerName, headerValue);
32376             }
32377         }
32378         
32379         var _this = this;
32380         
32381         this.xhr.onload = function()
32382         {
32383             _this.xhrOnLoad(_this.xhr);
32384         }
32385         
32386         this.xhr.onerror = function()
32387         {
32388             _this.xhrOnError(_this.xhr);
32389         }
32390         
32391         var formData = new FormData();
32392
32393         formData.append('returnHTML', 'NO');
32394         
32395         if(crop){
32396             formData.append('crop', crop);
32397         }
32398         
32399         formData.append(this.paramName, file, file.name);
32400         
32401         var options = {
32402             file : file, 
32403             manually : false
32404         };
32405         
32406         if(this.fireEvent('prepare', this, formData, options) != false){
32407             
32408             if(options.manually){
32409                 return;
32410             }
32411             
32412             this.xhr.send(formData);
32413             return;
32414         };
32415         
32416         this.uploadCancel();
32417     },
32418     
32419     uploadCancel : function()
32420     {
32421         if (this.xhr) {
32422             this.xhr.abort();
32423         }
32424         
32425         this.delegates = [];
32426         
32427         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32428             el.remove();
32429         }, this);
32430         
32431         this.arrange();
32432     },
32433     
32434     renderPreview : function(file)
32435     {
32436         if(typeof(file.target) != 'undefined' && file.target){
32437             return file;
32438         }
32439         
32440         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32441         
32442         var previewEl = this.managerEl.createChild({
32443             tag : 'div',
32444             cls : 'roo-document-manager-preview',
32445             cn : [
32446                 {
32447                     tag : 'div',
32448                     tooltip : file[this.toolTipName],
32449                     cls : 'roo-document-manager-thumb',
32450                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32451                 },
32452                 {
32453                     tag : 'button',
32454                     cls : 'close',
32455                     html : '<i class="fa fa-times-circle"></i>'
32456                 }
32457             ]
32458         });
32459
32460         var close = previewEl.select('button.close', true).first();
32461
32462         close.on('click', this.onRemove, this, file);
32463
32464         file.target = previewEl;
32465
32466         var image = previewEl.select('img', true).first();
32467         
32468         var _this = this;
32469         
32470         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32471         
32472         image.on('click', this.onClick, this, file);
32473         
32474         this.fireEvent('previewrendered', this, file);
32475         
32476         return file;
32477         
32478     },
32479     
32480     onPreviewLoad : function(file, image)
32481     {
32482         if(typeof(file.target) == 'undefined' || !file.target){
32483             return;
32484         }
32485         
32486         var width = image.dom.naturalWidth || image.dom.width;
32487         var height = image.dom.naturalHeight || image.dom.height;
32488         
32489         if(!this.previewResize) {
32490             return;
32491         }
32492         
32493         if(width > height){
32494             file.target.addClass('wide');
32495             return;
32496         }
32497         
32498         file.target.addClass('tall');
32499         return;
32500         
32501     },
32502     
32503     uploadFromSource : function(file, crop)
32504     {
32505         this.xhr = new XMLHttpRequest();
32506         
32507         this.managerEl.createChild({
32508             tag : 'div',
32509             cls : 'roo-document-manager-loading',
32510             cn : [
32511                 {
32512                     tag : 'div',
32513                     tooltip : file.name,
32514                     cls : 'roo-document-manager-thumb',
32515                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32516                 }
32517             ]
32518
32519         });
32520
32521         this.xhr.open(this.method, this.url, true);
32522         
32523         var headers = {
32524             "Accept": "application/json",
32525             "Cache-Control": "no-cache",
32526             "X-Requested-With": "XMLHttpRequest"
32527         };
32528         
32529         for (var headerName in headers) {
32530             var headerValue = headers[headerName];
32531             if (headerValue) {
32532                 this.xhr.setRequestHeader(headerName, headerValue);
32533             }
32534         }
32535         
32536         var _this = this;
32537         
32538         this.xhr.onload = function()
32539         {
32540             _this.xhrOnLoad(_this.xhr);
32541         }
32542         
32543         this.xhr.onerror = function()
32544         {
32545             _this.xhrOnError(_this.xhr);
32546         }
32547         
32548         var formData = new FormData();
32549
32550         formData.append('returnHTML', 'NO');
32551         
32552         formData.append('crop', crop);
32553         
32554         if(typeof(file.filename) != 'undefined'){
32555             formData.append('filename', file.filename);
32556         }
32557         
32558         if(typeof(file.mimetype) != 'undefined'){
32559             formData.append('mimetype', file.mimetype);
32560         }
32561         
32562         Roo.log(formData);
32563         
32564         if(this.fireEvent('prepare', this, formData) != false){
32565             this.xhr.send(formData);
32566         };
32567     }
32568 });
32569
32570 /*
32571 * Licence: LGPL
32572 */
32573
32574 /**
32575  * @class Roo.bootstrap.DocumentViewer
32576  * @extends Roo.bootstrap.Component
32577  * Bootstrap DocumentViewer class
32578  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32579  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32580  * 
32581  * @constructor
32582  * Create a new DocumentViewer
32583  * @param {Object} config The config object
32584  */
32585
32586 Roo.bootstrap.DocumentViewer = function(config){
32587     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32588     
32589     this.addEvents({
32590         /**
32591          * @event initial
32592          * Fire after initEvent
32593          * @param {Roo.bootstrap.DocumentViewer} this
32594          */
32595         "initial" : true,
32596         /**
32597          * @event click
32598          * Fire after click
32599          * @param {Roo.bootstrap.DocumentViewer} this
32600          */
32601         "click" : true,
32602         /**
32603          * @event download
32604          * Fire after download button
32605          * @param {Roo.bootstrap.DocumentViewer} this
32606          */
32607         "download" : true,
32608         /**
32609          * @event trash
32610          * Fire after trash button
32611          * @param {Roo.bootstrap.DocumentViewer} this
32612          */
32613         "trash" : true
32614         
32615     });
32616 };
32617
32618 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32619     
32620     showDownload : true,
32621     
32622     showTrash : true,
32623     
32624     getAutoCreate : function()
32625     {
32626         var cfg = {
32627             tag : 'div',
32628             cls : 'roo-document-viewer',
32629             cn : [
32630                 {
32631                     tag : 'div',
32632                     cls : 'roo-document-viewer-body',
32633                     cn : [
32634                         {
32635                             tag : 'div',
32636                             cls : 'roo-document-viewer-thumb',
32637                             cn : [
32638                                 {
32639                                     tag : 'img',
32640                                     cls : 'roo-document-viewer-image'
32641                                 }
32642                             ]
32643                         }
32644                     ]
32645                 },
32646                 {
32647                     tag : 'div',
32648                     cls : 'roo-document-viewer-footer',
32649                     cn : {
32650                         tag : 'div',
32651                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32652                         cn : [
32653                             {
32654                                 tag : 'div',
32655                                 cls : 'btn-group roo-document-viewer-download',
32656                                 cn : [
32657                                     {
32658                                         tag : 'button',
32659                                         cls : 'btn btn-default',
32660                                         html : '<i class="fa fa-download"></i>'
32661                                     }
32662                                 ]
32663                             },
32664                             {
32665                                 tag : 'div',
32666                                 cls : 'btn-group roo-document-viewer-trash',
32667                                 cn : [
32668                                     {
32669                                         tag : 'button',
32670                                         cls : 'btn btn-default',
32671                                         html : '<i class="fa fa-trash"></i>'
32672                                     }
32673                                 ]
32674                             }
32675                         ]
32676                     }
32677                 }
32678             ]
32679         };
32680         
32681         return cfg;
32682     },
32683     
32684     initEvents : function()
32685     {
32686         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32687         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32688         
32689         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32690         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32691         
32692         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32693         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32694         
32695         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32696         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32697         
32698         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32699         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32700         
32701         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32702         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32703         
32704         this.bodyEl.on('click', this.onClick, this);
32705         this.downloadBtn.on('click', this.onDownload, this);
32706         this.trashBtn.on('click', this.onTrash, this);
32707         
32708         this.downloadBtn.hide();
32709         this.trashBtn.hide();
32710         
32711         if(this.showDownload){
32712             this.downloadBtn.show();
32713         }
32714         
32715         if(this.showTrash){
32716             this.trashBtn.show();
32717         }
32718         
32719         if(!this.showDownload && !this.showTrash) {
32720             this.footerEl.hide();
32721         }
32722         
32723     },
32724     
32725     initial : function()
32726     {
32727         this.fireEvent('initial', this);
32728         
32729     },
32730     
32731     onClick : function(e)
32732     {
32733         e.preventDefault();
32734         
32735         this.fireEvent('click', this);
32736     },
32737     
32738     onDownload : function(e)
32739     {
32740         e.preventDefault();
32741         
32742         this.fireEvent('download', this);
32743     },
32744     
32745     onTrash : function(e)
32746     {
32747         e.preventDefault();
32748         
32749         this.fireEvent('trash', this);
32750     }
32751     
32752 });
32753 /*
32754  * - LGPL
32755  *
32756  * nav progress bar
32757  * 
32758  */
32759
32760 /**
32761  * @class Roo.bootstrap.NavProgressBar
32762  * @extends Roo.bootstrap.Component
32763  * Bootstrap NavProgressBar class
32764  * 
32765  * @constructor
32766  * Create a new nav progress bar
32767  * @param {Object} config The config object
32768  */
32769
32770 Roo.bootstrap.NavProgressBar = function(config){
32771     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32772
32773     this.bullets = this.bullets || [];
32774    
32775 //    Roo.bootstrap.NavProgressBar.register(this);
32776      this.addEvents({
32777         /**
32778              * @event changed
32779              * Fires when the active item changes
32780              * @param {Roo.bootstrap.NavProgressBar} this
32781              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32782              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32783          */
32784         'changed': true
32785      });
32786     
32787 };
32788
32789 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32790     
32791     bullets : [],
32792     barItems : [],
32793     
32794     getAutoCreate : function()
32795     {
32796         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32797         
32798         cfg = {
32799             tag : 'div',
32800             cls : 'roo-navigation-bar-group',
32801             cn : [
32802                 {
32803                     tag : 'div',
32804                     cls : 'roo-navigation-top-bar'
32805                 },
32806                 {
32807                     tag : 'div',
32808                     cls : 'roo-navigation-bullets-bar',
32809                     cn : [
32810                         {
32811                             tag : 'ul',
32812                             cls : 'roo-navigation-bar'
32813                         }
32814                     ]
32815                 },
32816                 
32817                 {
32818                     tag : 'div',
32819                     cls : 'roo-navigation-bottom-bar'
32820                 }
32821             ]
32822             
32823         };
32824         
32825         return cfg;
32826         
32827     },
32828     
32829     initEvents: function() 
32830     {
32831         
32832     },
32833     
32834     onRender : function(ct, position) 
32835     {
32836         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32837         
32838         if(this.bullets.length){
32839             Roo.each(this.bullets, function(b){
32840                this.addItem(b);
32841             }, this);
32842         }
32843         
32844         this.format();
32845         
32846     },
32847     
32848     addItem : function(cfg)
32849     {
32850         var item = new Roo.bootstrap.NavProgressItem(cfg);
32851         
32852         item.parentId = this.id;
32853         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32854         
32855         if(cfg.html){
32856             var top = new Roo.bootstrap.Element({
32857                 tag : 'div',
32858                 cls : 'roo-navigation-bar-text'
32859             });
32860             
32861             var bottom = new Roo.bootstrap.Element({
32862                 tag : 'div',
32863                 cls : 'roo-navigation-bar-text'
32864             });
32865             
32866             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32867             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32868             
32869             var topText = new Roo.bootstrap.Element({
32870                 tag : 'span',
32871                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32872             });
32873             
32874             var bottomText = new Roo.bootstrap.Element({
32875                 tag : 'span',
32876                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32877             });
32878             
32879             topText.onRender(top.el, null);
32880             bottomText.onRender(bottom.el, null);
32881             
32882             item.topEl = top;
32883             item.bottomEl = bottom;
32884         }
32885         
32886         this.barItems.push(item);
32887         
32888         return item;
32889     },
32890     
32891     getActive : function()
32892     {
32893         var active = false;
32894         
32895         Roo.each(this.barItems, function(v){
32896             
32897             if (!v.isActive()) {
32898                 return;
32899             }
32900             
32901             active = v;
32902             return false;
32903             
32904         });
32905         
32906         return active;
32907     },
32908     
32909     setActiveItem : function(item)
32910     {
32911         var prev = false;
32912         
32913         Roo.each(this.barItems, function(v){
32914             if (v.rid == item.rid) {
32915                 return ;
32916             }
32917             
32918             if (v.isActive()) {
32919                 v.setActive(false);
32920                 prev = v;
32921             }
32922         });
32923
32924         item.setActive(true);
32925         
32926         this.fireEvent('changed', this, item, prev);
32927     },
32928     
32929     getBarItem: function(rid)
32930     {
32931         var ret = false;
32932         
32933         Roo.each(this.barItems, function(e) {
32934             if (e.rid != rid) {
32935                 return;
32936             }
32937             
32938             ret =  e;
32939             return false;
32940         });
32941         
32942         return ret;
32943     },
32944     
32945     indexOfItem : function(item)
32946     {
32947         var index = false;
32948         
32949         Roo.each(this.barItems, function(v, i){
32950             
32951             if (v.rid != item.rid) {
32952                 return;
32953             }
32954             
32955             index = i;
32956             return false
32957         });
32958         
32959         return index;
32960     },
32961     
32962     setActiveNext : function()
32963     {
32964         var i = this.indexOfItem(this.getActive());
32965         
32966         if (i > this.barItems.length) {
32967             return;
32968         }
32969         
32970         this.setActiveItem(this.barItems[i+1]);
32971     },
32972     
32973     setActivePrev : function()
32974     {
32975         var i = this.indexOfItem(this.getActive());
32976         
32977         if (i  < 1) {
32978             return;
32979         }
32980         
32981         this.setActiveItem(this.barItems[i-1]);
32982     },
32983     
32984     format : function()
32985     {
32986         if(!this.barItems.length){
32987             return;
32988         }
32989      
32990         var width = 100 / this.barItems.length;
32991         
32992         Roo.each(this.barItems, function(i){
32993             i.el.setStyle('width', width + '%');
32994             i.topEl.el.setStyle('width', width + '%');
32995             i.bottomEl.el.setStyle('width', width + '%');
32996         }, this);
32997         
32998     }
32999     
33000 });
33001 /*
33002  * - LGPL
33003  *
33004  * Nav Progress Item
33005  * 
33006  */
33007
33008 /**
33009  * @class Roo.bootstrap.NavProgressItem
33010  * @extends Roo.bootstrap.Component
33011  * Bootstrap NavProgressItem class
33012  * @cfg {String} rid the reference id
33013  * @cfg {Boolean} active (true|false) Is item active default false
33014  * @cfg {Boolean} disabled (true|false) Is item active default false
33015  * @cfg {String} html
33016  * @cfg {String} position (top|bottom) text position default bottom
33017  * @cfg {String} icon show icon instead of number
33018  * 
33019  * @constructor
33020  * Create a new NavProgressItem
33021  * @param {Object} config The config object
33022  */
33023 Roo.bootstrap.NavProgressItem = function(config){
33024     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33025     this.addEvents({
33026         // raw events
33027         /**
33028          * @event click
33029          * The raw click event for the entire grid.
33030          * @param {Roo.bootstrap.NavProgressItem} this
33031          * @param {Roo.EventObject} e
33032          */
33033         "click" : true
33034     });
33035    
33036 };
33037
33038 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33039     
33040     rid : '',
33041     active : false,
33042     disabled : false,
33043     html : '',
33044     position : 'bottom',
33045     icon : false,
33046     
33047     getAutoCreate : function()
33048     {
33049         var iconCls = 'roo-navigation-bar-item-icon';
33050         
33051         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33052         
33053         var cfg = {
33054             tag: 'li',
33055             cls: 'roo-navigation-bar-item',
33056             cn : [
33057                 {
33058                     tag : 'i',
33059                     cls : iconCls
33060                 }
33061             ]
33062         };
33063         
33064         if(this.active){
33065             cfg.cls += ' active';
33066         }
33067         if(this.disabled){
33068             cfg.cls += ' disabled';
33069         }
33070         
33071         return cfg;
33072     },
33073     
33074     disable : function()
33075     {
33076         this.setDisabled(true);
33077     },
33078     
33079     enable : function()
33080     {
33081         this.setDisabled(false);
33082     },
33083     
33084     initEvents: function() 
33085     {
33086         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33087         
33088         this.iconEl.on('click', this.onClick, this);
33089     },
33090     
33091     onClick : function(e)
33092     {
33093         e.preventDefault();
33094         
33095         if(this.disabled){
33096             return;
33097         }
33098         
33099         if(this.fireEvent('click', this, e) === false){
33100             return;
33101         };
33102         
33103         this.parent().setActiveItem(this);
33104     },
33105     
33106     isActive: function () 
33107     {
33108         return this.active;
33109     },
33110     
33111     setActive : function(state)
33112     {
33113         if(this.active == state){
33114             return;
33115         }
33116         
33117         this.active = state;
33118         
33119         if (state) {
33120             this.el.addClass('active');
33121             return;
33122         }
33123         
33124         this.el.removeClass('active');
33125         
33126         return;
33127     },
33128     
33129     setDisabled : function(state)
33130     {
33131         if(this.disabled == state){
33132             return;
33133         }
33134         
33135         this.disabled = state;
33136         
33137         if (state) {
33138             this.el.addClass('disabled');
33139             return;
33140         }
33141         
33142         this.el.removeClass('disabled');
33143     },
33144     
33145     tooltipEl : function()
33146     {
33147         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33148     }
33149 });
33150  
33151
33152  /*
33153  * - LGPL
33154  *
33155  * FieldLabel
33156  * 
33157  */
33158
33159 /**
33160  * @class Roo.bootstrap.FieldLabel
33161  * @extends Roo.bootstrap.Component
33162  * Bootstrap FieldLabel class
33163  * @cfg {String} html contents of the element
33164  * @cfg {String} tag tag of the element default label
33165  * @cfg {String} cls class of the element
33166  * @cfg {String} target label target 
33167  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33168  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33169  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33170  * @cfg {String} iconTooltip default "This field is required"
33171  * @cfg {String} indicatorpos (left|right) default left
33172  * 
33173  * @constructor
33174  * Create a new FieldLabel
33175  * @param {Object} config The config object
33176  */
33177
33178 Roo.bootstrap.FieldLabel = function(config){
33179     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33180     
33181     this.addEvents({
33182             /**
33183              * @event invalid
33184              * Fires after the field has been marked as invalid.
33185              * @param {Roo.form.FieldLabel} this
33186              * @param {String} msg The validation message
33187              */
33188             invalid : true,
33189             /**
33190              * @event valid
33191              * Fires after the field has been validated with no errors.
33192              * @param {Roo.form.FieldLabel} this
33193              */
33194             valid : true
33195         });
33196 };
33197
33198 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33199     
33200     tag: 'label',
33201     cls: '',
33202     html: '',
33203     target: '',
33204     allowBlank : true,
33205     invalidClass : 'has-warning',
33206     validClass : 'has-success',
33207     iconTooltip : 'This field is required',
33208     indicatorpos : 'left',
33209     
33210     getAutoCreate : function(){
33211         
33212         var cls = "";
33213         if (!this.allowBlank) {
33214             cls  = "visible";
33215         }
33216         
33217         var cfg = {
33218             tag : this.tag,
33219             cls : 'roo-bootstrap-field-label ' + this.cls,
33220             for : this.target,
33221             cn : [
33222                 {
33223                     tag : 'i',
33224                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33225                     tooltip : this.iconTooltip
33226                 },
33227                 {
33228                     tag : 'span',
33229                     html : this.html
33230                 }
33231             ] 
33232         };
33233         
33234         if(this.indicatorpos == 'right'){
33235             var cfg = {
33236                 tag : this.tag,
33237                 cls : 'roo-bootstrap-field-label ' + this.cls,
33238                 for : this.target,
33239                 cn : [
33240                     {
33241                         tag : 'span',
33242                         html : this.html
33243                     },
33244                     {
33245                         tag : 'i',
33246                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33247                         tooltip : this.iconTooltip
33248                     }
33249                 ] 
33250             };
33251         }
33252         
33253         return cfg;
33254     },
33255     
33256     initEvents: function() 
33257     {
33258         Roo.bootstrap.Element.superclass.initEvents.call(this);
33259         
33260         this.indicator = this.indicatorEl();
33261         
33262         if(this.indicator){
33263             this.indicator.removeClass('visible');
33264             this.indicator.addClass('invisible');
33265         }
33266         
33267         Roo.bootstrap.FieldLabel.register(this);
33268     },
33269     
33270     indicatorEl : function()
33271     {
33272         var indicator = this.el.select('i.roo-required-indicator',true).first();
33273         
33274         if(!indicator){
33275             return false;
33276         }
33277         
33278         return indicator;
33279         
33280     },
33281     
33282     /**
33283      * Mark this field as valid
33284      */
33285     markValid : function()
33286     {
33287         if(this.indicator){
33288             this.indicator.removeClass('visible');
33289             this.indicator.addClass('invisible');
33290         }
33291         if (Roo.bootstrap.version == 3) {
33292             this.el.removeClass(this.invalidClass);
33293             this.el.addClass(this.validClass);
33294         } else {
33295             this.el.removeClass('is-invalid');
33296             this.el.addClass('is-valid');
33297         }
33298         
33299         
33300         this.fireEvent('valid', this);
33301     },
33302     
33303     /**
33304      * Mark this field as invalid
33305      * @param {String} msg The validation message
33306      */
33307     markInvalid : function(msg)
33308     {
33309         if(this.indicator){
33310             this.indicator.removeClass('invisible');
33311             this.indicator.addClass('visible');
33312         }
33313           if (Roo.bootstrap.version == 3) {
33314             this.el.removeClass(this.validClass);
33315             this.el.addClass(this.invalidClass);
33316         } else {
33317             this.el.removeClass('is-valid');
33318             this.el.addClass('is-invalid');
33319         }
33320         
33321         
33322         this.fireEvent('invalid', this, msg);
33323     }
33324     
33325    
33326 });
33327
33328 Roo.apply(Roo.bootstrap.FieldLabel, {
33329     
33330     groups: {},
33331     
33332      /**
33333     * register a FieldLabel Group
33334     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33335     */
33336     register : function(label)
33337     {
33338         if(this.groups.hasOwnProperty(label.target)){
33339             return;
33340         }
33341      
33342         this.groups[label.target] = label;
33343         
33344     },
33345     /**
33346     * fetch a FieldLabel Group based on the target
33347     * @param {string} target
33348     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33349     */
33350     get: function(target) {
33351         if (typeof(this.groups[target]) == 'undefined') {
33352             return false;
33353         }
33354         
33355         return this.groups[target] ;
33356     }
33357 });
33358
33359  
33360
33361  /*
33362  * - LGPL
33363  *
33364  * page DateSplitField.
33365  * 
33366  */
33367
33368
33369 /**
33370  * @class Roo.bootstrap.DateSplitField
33371  * @extends Roo.bootstrap.Component
33372  * Bootstrap DateSplitField class
33373  * @cfg {string} fieldLabel - the label associated
33374  * @cfg {Number} labelWidth set the width of label (0-12)
33375  * @cfg {String} labelAlign (top|left)
33376  * @cfg {Boolean} dayAllowBlank (true|false) default false
33377  * @cfg {Boolean} monthAllowBlank (true|false) default false
33378  * @cfg {Boolean} yearAllowBlank (true|false) default false
33379  * @cfg {string} dayPlaceholder 
33380  * @cfg {string} monthPlaceholder
33381  * @cfg {string} yearPlaceholder
33382  * @cfg {string} dayFormat default 'd'
33383  * @cfg {string} monthFormat default 'm'
33384  * @cfg {string} yearFormat default 'Y'
33385  * @cfg {Number} labellg set the width of label (1-12)
33386  * @cfg {Number} labelmd set the width of label (1-12)
33387  * @cfg {Number} labelsm set the width of label (1-12)
33388  * @cfg {Number} labelxs set the width of label (1-12)
33389
33390  *     
33391  * @constructor
33392  * Create a new DateSplitField
33393  * @param {Object} config The config object
33394  */
33395
33396 Roo.bootstrap.DateSplitField = function(config){
33397     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33398     
33399     this.addEvents({
33400         // raw events
33401          /**
33402          * @event years
33403          * getting the data of years
33404          * @param {Roo.bootstrap.DateSplitField} this
33405          * @param {Object} years
33406          */
33407         "years" : true,
33408         /**
33409          * @event days
33410          * getting the data of days
33411          * @param {Roo.bootstrap.DateSplitField} this
33412          * @param {Object} days
33413          */
33414         "days" : true,
33415         /**
33416          * @event invalid
33417          * Fires after the field has been marked as invalid.
33418          * @param {Roo.form.Field} this
33419          * @param {String} msg The validation message
33420          */
33421         invalid : true,
33422        /**
33423          * @event valid
33424          * Fires after the field has been validated with no errors.
33425          * @param {Roo.form.Field} this
33426          */
33427         valid : true
33428     });
33429 };
33430
33431 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33432     
33433     fieldLabel : '',
33434     labelAlign : 'top',
33435     labelWidth : 3,
33436     dayAllowBlank : false,
33437     monthAllowBlank : false,
33438     yearAllowBlank : false,
33439     dayPlaceholder : '',
33440     monthPlaceholder : '',
33441     yearPlaceholder : '',
33442     dayFormat : 'd',
33443     monthFormat : 'm',
33444     yearFormat : 'Y',
33445     isFormField : true,
33446     labellg : 0,
33447     labelmd : 0,
33448     labelsm : 0,
33449     labelxs : 0,
33450     
33451     getAutoCreate : function()
33452     {
33453         var cfg = {
33454             tag : 'div',
33455             cls : 'row roo-date-split-field-group',
33456             cn : [
33457                 {
33458                     tag : 'input',
33459                     type : 'hidden',
33460                     cls : 'form-hidden-field roo-date-split-field-group-value',
33461                     name : this.name
33462                 }
33463             ]
33464         };
33465         
33466         var labelCls = 'col-md-12';
33467         var contentCls = 'col-md-4';
33468         
33469         if(this.fieldLabel){
33470             
33471             var label = {
33472                 tag : 'div',
33473                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33474                 cn : [
33475                     {
33476                         tag : 'label',
33477                         html : this.fieldLabel
33478                     }
33479                 ]
33480             };
33481             
33482             if(this.labelAlign == 'left'){
33483             
33484                 if(this.labelWidth > 12){
33485                     label.style = "width: " + this.labelWidth + 'px';
33486                 }
33487
33488                 if(this.labelWidth < 13 && this.labelmd == 0){
33489                     this.labelmd = this.labelWidth;
33490                 }
33491
33492                 if(this.labellg > 0){
33493                     labelCls = ' col-lg-' + this.labellg;
33494                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33495                 }
33496
33497                 if(this.labelmd > 0){
33498                     labelCls = ' col-md-' + this.labelmd;
33499                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33500                 }
33501
33502                 if(this.labelsm > 0){
33503                     labelCls = ' col-sm-' + this.labelsm;
33504                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33505                 }
33506
33507                 if(this.labelxs > 0){
33508                     labelCls = ' col-xs-' + this.labelxs;
33509                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33510                 }
33511             }
33512             
33513             label.cls += ' ' + labelCls;
33514             
33515             cfg.cn.push(label);
33516         }
33517         
33518         Roo.each(['day', 'month', 'year'], function(t){
33519             cfg.cn.push({
33520                 tag : 'div',
33521                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33522             });
33523         }, this);
33524         
33525         return cfg;
33526     },
33527     
33528     inputEl: function ()
33529     {
33530         return this.el.select('.roo-date-split-field-group-value', true).first();
33531     },
33532     
33533     onRender : function(ct, position) 
33534     {
33535         var _this = this;
33536         
33537         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33538         
33539         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33540         
33541         this.dayField = new Roo.bootstrap.ComboBox({
33542             allowBlank : this.dayAllowBlank,
33543             alwaysQuery : true,
33544             displayField : 'value',
33545             editable : false,
33546             fieldLabel : '',
33547             forceSelection : true,
33548             mode : 'local',
33549             placeholder : this.dayPlaceholder,
33550             selectOnFocus : true,
33551             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33552             triggerAction : 'all',
33553             typeAhead : true,
33554             valueField : 'value',
33555             store : new Roo.data.SimpleStore({
33556                 data : (function() {    
33557                     var days = [];
33558                     _this.fireEvent('days', _this, days);
33559                     return days;
33560                 })(),
33561                 fields : [ 'value' ]
33562             }),
33563             listeners : {
33564                 select : function (_self, record, index)
33565                 {
33566                     _this.setValue(_this.getValue());
33567                 }
33568             }
33569         });
33570
33571         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33572         
33573         this.monthField = new Roo.bootstrap.MonthField({
33574             after : '<i class=\"fa fa-calendar\"></i>',
33575             allowBlank : this.monthAllowBlank,
33576             placeholder : this.monthPlaceholder,
33577             readOnly : true,
33578             listeners : {
33579                 render : function (_self)
33580                 {
33581                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33582                         e.preventDefault();
33583                         _self.focus();
33584                     });
33585                 },
33586                 select : function (_self, oldvalue, newvalue)
33587                 {
33588                     _this.setValue(_this.getValue());
33589                 }
33590             }
33591         });
33592         
33593         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33594         
33595         this.yearField = new Roo.bootstrap.ComboBox({
33596             allowBlank : this.yearAllowBlank,
33597             alwaysQuery : true,
33598             displayField : 'value',
33599             editable : false,
33600             fieldLabel : '',
33601             forceSelection : true,
33602             mode : 'local',
33603             placeholder : this.yearPlaceholder,
33604             selectOnFocus : true,
33605             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33606             triggerAction : 'all',
33607             typeAhead : true,
33608             valueField : 'value',
33609             store : new Roo.data.SimpleStore({
33610                 data : (function() {
33611                     var years = [];
33612                     _this.fireEvent('years', _this, years);
33613                     return years;
33614                 })(),
33615                 fields : [ 'value' ]
33616             }),
33617             listeners : {
33618                 select : function (_self, record, index)
33619                 {
33620                     _this.setValue(_this.getValue());
33621                 }
33622             }
33623         });
33624
33625         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33626     },
33627     
33628     setValue : function(v, format)
33629     {
33630         this.inputEl.dom.value = v;
33631         
33632         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33633         
33634         var d = Date.parseDate(v, f);
33635         
33636         if(!d){
33637             this.validate();
33638             return;
33639         }
33640         
33641         this.setDay(d.format(this.dayFormat));
33642         this.setMonth(d.format(this.monthFormat));
33643         this.setYear(d.format(this.yearFormat));
33644         
33645         this.validate();
33646         
33647         return;
33648     },
33649     
33650     setDay : function(v)
33651     {
33652         this.dayField.setValue(v);
33653         this.inputEl.dom.value = this.getValue();
33654         this.validate();
33655         return;
33656     },
33657     
33658     setMonth : function(v)
33659     {
33660         this.monthField.setValue(v, true);
33661         this.inputEl.dom.value = this.getValue();
33662         this.validate();
33663         return;
33664     },
33665     
33666     setYear : function(v)
33667     {
33668         this.yearField.setValue(v);
33669         this.inputEl.dom.value = this.getValue();
33670         this.validate();
33671         return;
33672     },
33673     
33674     getDay : function()
33675     {
33676         return this.dayField.getValue();
33677     },
33678     
33679     getMonth : function()
33680     {
33681         return this.monthField.getValue();
33682     },
33683     
33684     getYear : function()
33685     {
33686         return this.yearField.getValue();
33687     },
33688     
33689     getValue : function()
33690     {
33691         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33692         
33693         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33694         
33695         return date;
33696     },
33697     
33698     reset : function()
33699     {
33700         this.setDay('');
33701         this.setMonth('');
33702         this.setYear('');
33703         this.inputEl.dom.value = '';
33704         this.validate();
33705         return;
33706     },
33707     
33708     validate : function()
33709     {
33710         var d = this.dayField.validate();
33711         var m = this.monthField.validate();
33712         var y = this.yearField.validate();
33713         
33714         var valid = true;
33715         
33716         if(
33717                 (!this.dayAllowBlank && !d) ||
33718                 (!this.monthAllowBlank && !m) ||
33719                 (!this.yearAllowBlank && !y)
33720         ){
33721             valid = false;
33722         }
33723         
33724         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33725             return valid;
33726         }
33727         
33728         if(valid){
33729             this.markValid();
33730             return valid;
33731         }
33732         
33733         this.markInvalid();
33734         
33735         return valid;
33736     },
33737     
33738     markValid : function()
33739     {
33740         
33741         var label = this.el.select('label', true).first();
33742         var icon = this.el.select('i.fa-star', true).first();
33743
33744         if(label && icon){
33745             icon.remove();
33746         }
33747         
33748         this.fireEvent('valid', this);
33749     },
33750     
33751      /**
33752      * Mark this field as invalid
33753      * @param {String} msg The validation message
33754      */
33755     markInvalid : function(msg)
33756     {
33757         
33758         var label = this.el.select('label', true).first();
33759         var icon = this.el.select('i.fa-star', true).first();
33760
33761         if(label && !icon){
33762             this.el.select('.roo-date-split-field-label', true).createChild({
33763                 tag : 'i',
33764                 cls : 'text-danger fa fa-lg fa-star',
33765                 tooltip : 'This field is required',
33766                 style : 'margin-right:5px;'
33767             }, label, true);
33768         }
33769         
33770         this.fireEvent('invalid', this, msg);
33771     },
33772     
33773     clearInvalid : function()
33774     {
33775         var label = this.el.select('label', true).first();
33776         var icon = this.el.select('i.fa-star', true).first();
33777
33778         if(label && icon){
33779             icon.remove();
33780         }
33781         
33782         this.fireEvent('valid', this);
33783     },
33784     
33785     getName: function()
33786     {
33787         return this.name;
33788     }
33789     
33790 });
33791
33792  /**
33793  *
33794  * This is based on 
33795  * http://masonry.desandro.com
33796  *
33797  * The idea is to render all the bricks based on vertical width...
33798  *
33799  * The original code extends 'outlayer' - we might need to use that....
33800  * 
33801  */
33802
33803
33804 /**
33805  * @class Roo.bootstrap.LayoutMasonry
33806  * @extends Roo.bootstrap.Component
33807  * Bootstrap Layout Masonry class
33808  * 
33809  * @constructor
33810  * Create a new Element
33811  * @param {Object} config The config object
33812  */
33813
33814 Roo.bootstrap.LayoutMasonry = function(config){
33815     
33816     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33817     
33818     this.bricks = [];
33819     
33820     Roo.bootstrap.LayoutMasonry.register(this);
33821     
33822     this.addEvents({
33823         // raw events
33824         /**
33825          * @event layout
33826          * Fire after layout the items
33827          * @param {Roo.bootstrap.LayoutMasonry} this
33828          * @param {Roo.EventObject} e
33829          */
33830         "layout" : true
33831     });
33832     
33833 };
33834
33835 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33836     
33837     /**
33838      * @cfg {Boolean} isLayoutInstant = no animation?
33839      */   
33840     isLayoutInstant : false, // needed?
33841    
33842     /**
33843      * @cfg {Number} boxWidth  width of the columns
33844      */   
33845     boxWidth : 450,
33846     
33847       /**
33848      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33849      */   
33850     boxHeight : 0,
33851     
33852     /**
33853      * @cfg {Number} padWidth padding below box..
33854      */   
33855     padWidth : 10, 
33856     
33857     /**
33858      * @cfg {Number} gutter gutter width..
33859      */   
33860     gutter : 10,
33861     
33862      /**
33863      * @cfg {Number} maxCols maximum number of columns
33864      */   
33865     
33866     maxCols: 0,
33867     
33868     /**
33869      * @cfg {Boolean} isAutoInitial defalut true
33870      */   
33871     isAutoInitial : true, 
33872     
33873     containerWidth: 0,
33874     
33875     /**
33876      * @cfg {Boolean} isHorizontal defalut false
33877      */   
33878     isHorizontal : false, 
33879
33880     currentSize : null,
33881     
33882     tag: 'div',
33883     
33884     cls: '',
33885     
33886     bricks: null, //CompositeElement
33887     
33888     cols : 1,
33889     
33890     _isLayoutInited : false,
33891     
33892 //    isAlternative : false, // only use for vertical layout...
33893     
33894     /**
33895      * @cfg {Number} alternativePadWidth padding below box..
33896      */   
33897     alternativePadWidth : 50,
33898     
33899     selectedBrick : [],
33900     
33901     getAutoCreate : function(){
33902         
33903         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33904         
33905         var cfg = {
33906             tag: this.tag,
33907             cls: 'blog-masonary-wrapper ' + this.cls,
33908             cn : {
33909                 cls : 'mas-boxes masonary'
33910             }
33911         };
33912         
33913         return cfg;
33914     },
33915     
33916     getChildContainer: function( )
33917     {
33918         if (this.boxesEl) {
33919             return this.boxesEl;
33920         }
33921         
33922         this.boxesEl = this.el.select('.mas-boxes').first();
33923         
33924         return this.boxesEl;
33925     },
33926     
33927     
33928     initEvents : function()
33929     {
33930         var _this = this;
33931         
33932         if(this.isAutoInitial){
33933             Roo.log('hook children rendered');
33934             this.on('childrenrendered', function() {
33935                 Roo.log('children rendered');
33936                 _this.initial();
33937             } ,this);
33938         }
33939     },
33940     
33941     initial : function()
33942     {
33943         this.selectedBrick = [];
33944         
33945         this.currentSize = this.el.getBox(true);
33946         
33947         Roo.EventManager.onWindowResize(this.resize, this); 
33948
33949         if(!this.isAutoInitial){
33950             this.layout();
33951             return;
33952         }
33953         
33954         this.layout();
33955         
33956         return;
33957         //this.layout.defer(500,this);
33958         
33959     },
33960     
33961     resize : function()
33962     {
33963         var cs = this.el.getBox(true);
33964         
33965         if (
33966                 this.currentSize.width == cs.width && 
33967                 this.currentSize.x == cs.x && 
33968                 this.currentSize.height == cs.height && 
33969                 this.currentSize.y == cs.y 
33970         ) {
33971             Roo.log("no change in with or X or Y");
33972             return;
33973         }
33974         
33975         this.currentSize = cs;
33976         
33977         this.layout();
33978         
33979     },
33980     
33981     layout : function()
33982     {   
33983         this._resetLayout();
33984         
33985         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33986         
33987         this.layoutItems( isInstant );
33988       
33989         this._isLayoutInited = true;
33990         
33991         this.fireEvent('layout', this);
33992         
33993     },
33994     
33995     _resetLayout : function()
33996     {
33997         if(this.isHorizontal){
33998             this.horizontalMeasureColumns();
33999             return;
34000         }
34001         
34002         this.verticalMeasureColumns();
34003         
34004     },
34005     
34006     verticalMeasureColumns : function()
34007     {
34008         this.getContainerWidth();
34009         
34010 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34011 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34012 //            return;
34013 //        }
34014         
34015         var boxWidth = this.boxWidth + this.padWidth;
34016         
34017         if(this.containerWidth < this.boxWidth){
34018             boxWidth = this.containerWidth
34019         }
34020         
34021         var containerWidth = this.containerWidth;
34022         
34023         var cols = Math.floor(containerWidth / boxWidth);
34024         
34025         this.cols = Math.max( cols, 1 );
34026         
34027         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34028         
34029         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34030         
34031         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34032         
34033         this.colWidth = boxWidth + avail - this.padWidth;
34034         
34035         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34036         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34037     },
34038     
34039     horizontalMeasureColumns : function()
34040     {
34041         this.getContainerWidth();
34042         
34043         var boxWidth = this.boxWidth;
34044         
34045         if(this.containerWidth < boxWidth){
34046             boxWidth = this.containerWidth;
34047         }
34048         
34049         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34050         
34051         this.el.setHeight(boxWidth);
34052         
34053     },
34054     
34055     getContainerWidth : function()
34056     {
34057         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34058     },
34059     
34060     layoutItems : function( isInstant )
34061     {
34062         Roo.log(this.bricks);
34063         
34064         var items = Roo.apply([], this.bricks);
34065         
34066         if(this.isHorizontal){
34067             this._horizontalLayoutItems( items , isInstant );
34068             return;
34069         }
34070         
34071 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34072 //            this._verticalAlternativeLayoutItems( items , isInstant );
34073 //            return;
34074 //        }
34075         
34076         this._verticalLayoutItems( items , isInstant );
34077         
34078     },
34079     
34080     _verticalLayoutItems : function ( items , isInstant)
34081     {
34082         if ( !items || !items.length ) {
34083             return;
34084         }
34085         
34086         var standard = [
34087             ['xs', 'xs', 'xs', 'tall'],
34088             ['xs', 'xs', 'tall'],
34089             ['xs', 'xs', 'sm'],
34090             ['xs', 'xs', 'xs'],
34091             ['xs', 'tall'],
34092             ['xs', 'sm'],
34093             ['xs', 'xs'],
34094             ['xs'],
34095             
34096             ['sm', 'xs', 'xs'],
34097             ['sm', 'xs'],
34098             ['sm'],
34099             
34100             ['tall', 'xs', 'xs', 'xs'],
34101             ['tall', 'xs', 'xs'],
34102             ['tall', 'xs'],
34103             ['tall']
34104             
34105         ];
34106         
34107         var queue = [];
34108         
34109         var boxes = [];
34110         
34111         var box = [];
34112         
34113         Roo.each(items, function(item, k){
34114             
34115             switch (item.size) {
34116                 // these layouts take up a full box,
34117                 case 'md' :
34118                 case 'md-left' :
34119                 case 'md-right' :
34120                 case 'wide' :
34121                     
34122                     if(box.length){
34123                         boxes.push(box);
34124                         box = [];
34125                     }
34126                     
34127                     boxes.push([item]);
34128                     
34129                     break;
34130                     
34131                 case 'xs' :
34132                 case 'sm' :
34133                 case 'tall' :
34134                     
34135                     box.push(item);
34136                     
34137                     break;
34138                 default :
34139                     break;
34140                     
34141             }
34142             
34143         }, this);
34144         
34145         if(box.length){
34146             boxes.push(box);
34147             box = [];
34148         }
34149         
34150         var filterPattern = function(box, length)
34151         {
34152             if(!box.length){
34153                 return;
34154             }
34155             
34156             var match = false;
34157             
34158             var pattern = box.slice(0, length);
34159             
34160             var format = [];
34161             
34162             Roo.each(pattern, function(i){
34163                 format.push(i.size);
34164             }, this);
34165             
34166             Roo.each(standard, function(s){
34167                 
34168                 if(String(s) != String(format)){
34169                     return;
34170                 }
34171                 
34172                 match = true;
34173                 return false;
34174                 
34175             }, this);
34176             
34177             if(!match && length == 1){
34178                 return;
34179             }
34180             
34181             if(!match){
34182                 filterPattern(box, length - 1);
34183                 return;
34184             }
34185                 
34186             queue.push(pattern);
34187
34188             box = box.slice(length, box.length);
34189
34190             filterPattern(box, 4);
34191
34192             return;
34193             
34194         }
34195         
34196         Roo.each(boxes, function(box, k){
34197             
34198             if(!box.length){
34199                 return;
34200             }
34201             
34202             if(box.length == 1){
34203                 queue.push(box);
34204                 return;
34205             }
34206             
34207             filterPattern(box, 4);
34208             
34209         }, this);
34210         
34211         this._processVerticalLayoutQueue( queue, isInstant );
34212         
34213     },
34214     
34215 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34216 //    {
34217 //        if ( !items || !items.length ) {
34218 //            return;
34219 //        }
34220 //
34221 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34222 //        
34223 //    },
34224     
34225     _horizontalLayoutItems : function ( items , isInstant)
34226     {
34227         if ( !items || !items.length || items.length < 3) {
34228             return;
34229         }
34230         
34231         items.reverse();
34232         
34233         var eItems = items.slice(0, 3);
34234         
34235         items = items.slice(3, items.length);
34236         
34237         var standard = [
34238             ['xs', 'xs', 'xs', 'wide'],
34239             ['xs', 'xs', 'wide'],
34240             ['xs', 'xs', 'sm'],
34241             ['xs', 'xs', 'xs'],
34242             ['xs', 'wide'],
34243             ['xs', 'sm'],
34244             ['xs', 'xs'],
34245             ['xs'],
34246             
34247             ['sm', 'xs', 'xs'],
34248             ['sm', 'xs'],
34249             ['sm'],
34250             
34251             ['wide', 'xs', 'xs', 'xs'],
34252             ['wide', 'xs', 'xs'],
34253             ['wide', 'xs'],
34254             ['wide'],
34255             
34256             ['wide-thin']
34257         ];
34258         
34259         var queue = [];
34260         
34261         var boxes = [];
34262         
34263         var box = [];
34264         
34265         Roo.each(items, function(item, k){
34266             
34267             switch (item.size) {
34268                 case 'md' :
34269                 case 'md-left' :
34270                 case 'md-right' :
34271                 case 'tall' :
34272                     
34273                     if(box.length){
34274                         boxes.push(box);
34275                         box = [];
34276                     }
34277                     
34278                     boxes.push([item]);
34279                     
34280                     break;
34281                     
34282                 case 'xs' :
34283                 case 'sm' :
34284                 case 'wide' :
34285                 case 'wide-thin' :
34286                     
34287                     box.push(item);
34288                     
34289                     break;
34290                 default :
34291                     break;
34292                     
34293             }
34294             
34295         }, this);
34296         
34297         if(box.length){
34298             boxes.push(box);
34299             box = [];
34300         }
34301         
34302         var filterPattern = function(box, length)
34303         {
34304             if(!box.length){
34305                 return;
34306             }
34307             
34308             var match = false;
34309             
34310             var pattern = box.slice(0, length);
34311             
34312             var format = [];
34313             
34314             Roo.each(pattern, function(i){
34315                 format.push(i.size);
34316             }, this);
34317             
34318             Roo.each(standard, function(s){
34319                 
34320                 if(String(s) != String(format)){
34321                     return;
34322                 }
34323                 
34324                 match = true;
34325                 return false;
34326                 
34327             }, this);
34328             
34329             if(!match && length == 1){
34330                 return;
34331             }
34332             
34333             if(!match){
34334                 filterPattern(box, length - 1);
34335                 return;
34336             }
34337                 
34338             queue.push(pattern);
34339
34340             box = box.slice(length, box.length);
34341
34342             filterPattern(box, 4);
34343
34344             return;
34345             
34346         }
34347         
34348         Roo.each(boxes, function(box, k){
34349             
34350             if(!box.length){
34351                 return;
34352             }
34353             
34354             if(box.length == 1){
34355                 queue.push(box);
34356                 return;
34357             }
34358             
34359             filterPattern(box, 4);
34360             
34361         }, this);
34362         
34363         
34364         var prune = [];
34365         
34366         var pos = this.el.getBox(true);
34367         
34368         var minX = pos.x;
34369         
34370         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34371         
34372         var hit_end = false;
34373         
34374         Roo.each(queue, function(box){
34375             
34376             if(hit_end){
34377                 
34378                 Roo.each(box, function(b){
34379                 
34380                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34381                     b.el.hide();
34382
34383                 }, this);
34384
34385                 return;
34386             }
34387             
34388             var mx = 0;
34389             
34390             Roo.each(box, function(b){
34391                 
34392                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34393                 b.el.show();
34394
34395                 mx = Math.max(mx, b.x);
34396                 
34397             }, this);
34398             
34399             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34400             
34401             if(maxX < minX){
34402                 
34403                 Roo.each(box, function(b){
34404                 
34405                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34406                     b.el.hide();
34407                     
34408                 }, this);
34409                 
34410                 hit_end = true;
34411                 
34412                 return;
34413             }
34414             
34415             prune.push(box);
34416             
34417         }, this);
34418         
34419         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34420     },
34421     
34422     /** Sets position of item in DOM
34423     * @param {Element} item
34424     * @param {Number} x - horizontal position
34425     * @param {Number} y - vertical position
34426     * @param {Boolean} isInstant - disables transitions
34427     */
34428     _processVerticalLayoutQueue : function( queue, isInstant )
34429     {
34430         var pos = this.el.getBox(true);
34431         var x = pos.x;
34432         var y = pos.y;
34433         var maxY = [];
34434         
34435         for (var i = 0; i < this.cols; i++){
34436             maxY[i] = pos.y;
34437         }
34438         
34439         Roo.each(queue, function(box, k){
34440             
34441             var col = k % this.cols;
34442             
34443             Roo.each(box, function(b,kk){
34444                 
34445                 b.el.position('absolute');
34446                 
34447                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34448                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34449                 
34450                 if(b.size == 'md-left' || b.size == 'md-right'){
34451                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34452                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34453                 }
34454                 
34455                 b.el.setWidth(width);
34456                 b.el.setHeight(height);
34457                 // iframe?
34458                 b.el.select('iframe',true).setSize(width,height);
34459                 
34460             }, this);
34461             
34462             for (var i = 0; i < this.cols; i++){
34463                 
34464                 if(maxY[i] < maxY[col]){
34465                     col = i;
34466                     continue;
34467                 }
34468                 
34469                 col = Math.min(col, i);
34470                 
34471             }
34472             
34473             x = pos.x + col * (this.colWidth + this.padWidth);
34474             
34475             y = maxY[col];
34476             
34477             var positions = [];
34478             
34479             switch (box.length){
34480                 case 1 :
34481                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34482                     break;
34483                 case 2 :
34484                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34485                     break;
34486                 case 3 :
34487                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34488                     break;
34489                 case 4 :
34490                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34491                     break;
34492                 default :
34493                     break;
34494             }
34495             
34496             Roo.each(box, function(b,kk){
34497                 
34498                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34499                 
34500                 var sz = b.el.getSize();
34501                 
34502                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34503                 
34504             }, this);
34505             
34506         }, this);
34507         
34508         var mY = 0;
34509         
34510         for (var i = 0; i < this.cols; i++){
34511             mY = Math.max(mY, maxY[i]);
34512         }
34513         
34514         this.el.setHeight(mY - pos.y);
34515         
34516     },
34517     
34518 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34519 //    {
34520 //        var pos = this.el.getBox(true);
34521 //        var x = pos.x;
34522 //        var y = pos.y;
34523 //        var maxX = pos.right;
34524 //        
34525 //        var maxHeight = 0;
34526 //        
34527 //        Roo.each(items, function(item, k){
34528 //            
34529 //            var c = k % 2;
34530 //            
34531 //            item.el.position('absolute');
34532 //                
34533 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34534 //
34535 //            item.el.setWidth(width);
34536 //
34537 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34538 //
34539 //            item.el.setHeight(height);
34540 //            
34541 //            if(c == 0){
34542 //                item.el.setXY([x, y], isInstant ? false : true);
34543 //            } else {
34544 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34545 //            }
34546 //            
34547 //            y = y + height + this.alternativePadWidth;
34548 //            
34549 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34550 //            
34551 //        }, this);
34552 //        
34553 //        this.el.setHeight(maxHeight);
34554 //        
34555 //    },
34556     
34557     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34558     {
34559         var pos = this.el.getBox(true);
34560         
34561         var minX = pos.x;
34562         var minY = pos.y;
34563         
34564         var maxX = pos.right;
34565         
34566         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34567         
34568         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34569         
34570         Roo.each(queue, function(box, k){
34571             
34572             Roo.each(box, function(b, kk){
34573                 
34574                 b.el.position('absolute');
34575                 
34576                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34577                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34578                 
34579                 if(b.size == 'md-left' || b.size == 'md-right'){
34580                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34581                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34582                 }
34583                 
34584                 b.el.setWidth(width);
34585                 b.el.setHeight(height);
34586                 
34587             }, this);
34588             
34589             if(!box.length){
34590                 return;
34591             }
34592             
34593             var positions = [];
34594             
34595             switch (box.length){
34596                 case 1 :
34597                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34598                     break;
34599                 case 2 :
34600                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34601                     break;
34602                 case 3 :
34603                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34604                     break;
34605                 case 4 :
34606                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34607                     break;
34608                 default :
34609                     break;
34610             }
34611             
34612             Roo.each(box, function(b,kk){
34613                 
34614                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34615                 
34616                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34617                 
34618             }, this);
34619             
34620         }, this);
34621         
34622     },
34623     
34624     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34625     {
34626         Roo.each(eItems, function(b,k){
34627             
34628             b.size = (k == 0) ? 'sm' : 'xs';
34629             b.x = (k == 0) ? 2 : 1;
34630             b.y = (k == 0) ? 2 : 1;
34631             
34632             b.el.position('absolute');
34633             
34634             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34635                 
34636             b.el.setWidth(width);
34637             
34638             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34639             
34640             b.el.setHeight(height);
34641             
34642         }, this);
34643
34644         var positions = [];
34645         
34646         positions.push({
34647             x : maxX - this.unitWidth * 2 - this.gutter,
34648             y : minY
34649         });
34650         
34651         positions.push({
34652             x : maxX - this.unitWidth,
34653             y : minY + (this.unitWidth + this.gutter) * 2
34654         });
34655         
34656         positions.push({
34657             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34658             y : minY
34659         });
34660         
34661         Roo.each(eItems, function(b,k){
34662             
34663             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34664
34665         }, this);
34666         
34667     },
34668     
34669     getVerticalOneBoxColPositions : function(x, y, box)
34670     {
34671         var pos = [];
34672         
34673         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34674         
34675         if(box[0].size == 'md-left'){
34676             rand = 0;
34677         }
34678         
34679         if(box[0].size == 'md-right'){
34680             rand = 1;
34681         }
34682         
34683         pos.push({
34684             x : x + (this.unitWidth + this.gutter) * rand,
34685             y : y
34686         });
34687         
34688         return pos;
34689     },
34690     
34691     getVerticalTwoBoxColPositions : function(x, y, box)
34692     {
34693         var pos = [];
34694         
34695         if(box[0].size == 'xs'){
34696             
34697             pos.push({
34698                 x : x,
34699                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34700             });
34701
34702             pos.push({
34703                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34704                 y : y
34705             });
34706             
34707             return pos;
34708             
34709         }
34710         
34711         pos.push({
34712             x : x,
34713             y : y
34714         });
34715
34716         pos.push({
34717             x : x + (this.unitWidth + this.gutter) * 2,
34718             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34719         });
34720         
34721         return pos;
34722         
34723     },
34724     
34725     getVerticalThreeBoxColPositions : function(x, y, box)
34726     {
34727         var pos = [];
34728         
34729         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34730             
34731             pos.push({
34732                 x : x,
34733                 y : y
34734             });
34735
34736             pos.push({
34737                 x : x + (this.unitWidth + this.gutter) * 1,
34738                 y : y
34739             });
34740             
34741             pos.push({
34742                 x : x + (this.unitWidth + this.gutter) * 2,
34743                 y : y
34744             });
34745             
34746             return pos;
34747             
34748         }
34749         
34750         if(box[0].size == 'xs' && box[1].size == 'xs'){
34751             
34752             pos.push({
34753                 x : x,
34754                 y : y
34755             });
34756
34757             pos.push({
34758                 x : x,
34759                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34760             });
34761             
34762             pos.push({
34763                 x : x + (this.unitWidth + this.gutter) * 1,
34764                 y : y
34765             });
34766             
34767             return pos;
34768             
34769         }
34770         
34771         pos.push({
34772             x : x,
34773             y : y
34774         });
34775
34776         pos.push({
34777             x : x + (this.unitWidth + this.gutter) * 2,
34778             y : y
34779         });
34780
34781         pos.push({
34782             x : x + (this.unitWidth + this.gutter) * 2,
34783             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34784         });
34785             
34786         return pos;
34787         
34788     },
34789     
34790     getVerticalFourBoxColPositions : function(x, y, box)
34791     {
34792         var pos = [];
34793         
34794         if(box[0].size == 'xs'){
34795             
34796             pos.push({
34797                 x : x,
34798                 y : y
34799             });
34800
34801             pos.push({
34802                 x : x,
34803                 y : y + (this.unitHeight + this.gutter) * 1
34804             });
34805             
34806             pos.push({
34807                 x : x,
34808                 y : y + (this.unitHeight + this.gutter) * 2
34809             });
34810             
34811             pos.push({
34812                 x : x + (this.unitWidth + this.gutter) * 1,
34813                 y : y
34814             });
34815             
34816             return pos;
34817             
34818         }
34819         
34820         pos.push({
34821             x : x,
34822             y : y
34823         });
34824
34825         pos.push({
34826             x : x + (this.unitWidth + this.gutter) * 2,
34827             y : y
34828         });
34829
34830         pos.push({
34831             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34832             y : y + (this.unitHeight + this.gutter) * 1
34833         });
34834
34835         pos.push({
34836             x : x + (this.unitWidth + this.gutter) * 2,
34837             y : y + (this.unitWidth + this.gutter) * 2
34838         });
34839
34840         return pos;
34841         
34842     },
34843     
34844     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34845     {
34846         var pos = [];
34847         
34848         if(box[0].size == 'md-left'){
34849             pos.push({
34850                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34851                 y : minY
34852             });
34853             
34854             return pos;
34855         }
34856         
34857         if(box[0].size == 'md-right'){
34858             pos.push({
34859                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34860                 y : minY + (this.unitWidth + this.gutter) * 1
34861             });
34862             
34863             return pos;
34864         }
34865         
34866         var rand = Math.floor(Math.random() * (4 - box[0].y));
34867         
34868         pos.push({
34869             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34870             y : minY + (this.unitWidth + this.gutter) * rand
34871         });
34872         
34873         return pos;
34874         
34875     },
34876     
34877     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34878     {
34879         var pos = [];
34880         
34881         if(box[0].size == 'xs'){
34882             
34883             pos.push({
34884                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34885                 y : minY
34886             });
34887
34888             pos.push({
34889                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34890                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34891             });
34892             
34893             return pos;
34894             
34895         }
34896         
34897         pos.push({
34898             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34899             y : minY
34900         });
34901
34902         pos.push({
34903             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34904             y : minY + (this.unitWidth + this.gutter) * 2
34905         });
34906         
34907         return pos;
34908         
34909     },
34910     
34911     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34912     {
34913         var pos = [];
34914         
34915         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34916             
34917             pos.push({
34918                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34919                 y : minY
34920             });
34921
34922             pos.push({
34923                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34924                 y : minY + (this.unitWidth + this.gutter) * 1
34925             });
34926             
34927             pos.push({
34928                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34929                 y : minY + (this.unitWidth + this.gutter) * 2
34930             });
34931             
34932             return pos;
34933             
34934         }
34935         
34936         if(box[0].size == 'xs' && box[1].size == 'xs'){
34937             
34938             pos.push({
34939                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34940                 y : minY
34941             });
34942
34943             pos.push({
34944                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34945                 y : minY
34946             });
34947             
34948             pos.push({
34949                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34950                 y : minY + (this.unitWidth + this.gutter) * 1
34951             });
34952             
34953             return pos;
34954             
34955         }
34956         
34957         pos.push({
34958             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34959             y : minY
34960         });
34961
34962         pos.push({
34963             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34964             y : minY + (this.unitWidth + this.gutter) * 2
34965         });
34966
34967         pos.push({
34968             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34969             y : minY + (this.unitWidth + this.gutter) * 2
34970         });
34971             
34972         return pos;
34973         
34974     },
34975     
34976     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34977     {
34978         var pos = [];
34979         
34980         if(box[0].size == 'xs'){
34981             
34982             pos.push({
34983                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34984                 y : minY
34985             });
34986
34987             pos.push({
34988                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34989                 y : minY
34990             });
34991             
34992             pos.push({
34993                 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),
34994                 y : minY
34995             });
34996             
34997             pos.push({
34998                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34999                 y : minY + (this.unitWidth + this.gutter) * 1
35000             });
35001             
35002             return pos;
35003             
35004         }
35005         
35006         pos.push({
35007             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35008             y : minY
35009         });
35010         
35011         pos.push({
35012             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35013             y : minY + (this.unitWidth + this.gutter) * 2
35014         });
35015         
35016         pos.push({
35017             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35018             y : minY + (this.unitWidth + this.gutter) * 2
35019         });
35020         
35021         pos.push({
35022             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),
35023             y : minY + (this.unitWidth + this.gutter) * 2
35024         });
35025
35026         return pos;
35027         
35028     },
35029     
35030     /**
35031     * remove a Masonry Brick
35032     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35033     */
35034     removeBrick : function(brick_id)
35035     {
35036         if (!brick_id) {
35037             return;
35038         }
35039         
35040         for (var i = 0; i<this.bricks.length; i++) {
35041             if (this.bricks[i].id == brick_id) {
35042                 this.bricks.splice(i,1);
35043                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35044                 this.initial();
35045             }
35046         }
35047     },
35048     
35049     /**
35050     * adds a Masonry Brick
35051     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35052     */
35053     addBrick : function(cfg)
35054     {
35055         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35056         //this.register(cn);
35057         cn.parentId = this.id;
35058         cn.render(this.el);
35059         return cn;
35060     },
35061     
35062     /**
35063     * register a Masonry Brick
35064     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35065     */
35066     
35067     register : function(brick)
35068     {
35069         this.bricks.push(brick);
35070         brick.masonryId = this.id;
35071     },
35072     
35073     /**
35074     * clear all the Masonry Brick
35075     */
35076     clearAll : function()
35077     {
35078         this.bricks = [];
35079         //this.getChildContainer().dom.innerHTML = "";
35080         this.el.dom.innerHTML = '';
35081     },
35082     
35083     getSelected : function()
35084     {
35085         if (!this.selectedBrick) {
35086             return false;
35087         }
35088         
35089         return this.selectedBrick;
35090     }
35091 });
35092
35093 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35094     
35095     groups: {},
35096      /**
35097     * register a Masonry Layout
35098     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35099     */
35100     
35101     register : function(layout)
35102     {
35103         this.groups[layout.id] = layout;
35104     },
35105     /**
35106     * fetch a  Masonry Layout based on the masonry layout ID
35107     * @param {string} the masonry layout to add
35108     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35109     */
35110     
35111     get: function(layout_id) {
35112         if (typeof(this.groups[layout_id]) == 'undefined') {
35113             return false;
35114         }
35115         return this.groups[layout_id] ;
35116     }
35117     
35118     
35119     
35120 });
35121
35122  
35123
35124  /**
35125  *
35126  * This is based on 
35127  * http://masonry.desandro.com
35128  *
35129  * The idea is to render all the bricks based on vertical width...
35130  *
35131  * The original code extends 'outlayer' - we might need to use that....
35132  * 
35133  */
35134
35135
35136 /**
35137  * @class Roo.bootstrap.LayoutMasonryAuto
35138  * @extends Roo.bootstrap.Component
35139  * Bootstrap Layout Masonry class
35140  * 
35141  * @constructor
35142  * Create a new Element
35143  * @param {Object} config The config object
35144  */
35145
35146 Roo.bootstrap.LayoutMasonryAuto = function(config){
35147     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35148 };
35149
35150 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35151     
35152       /**
35153      * @cfg {Boolean} isFitWidth  - resize the width..
35154      */   
35155     isFitWidth : false,  // options..
35156     /**
35157      * @cfg {Boolean} isOriginLeft = left align?
35158      */   
35159     isOriginLeft : true,
35160     /**
35161      * @cfg {Boolean} isOriginTop = top align?
35162      */   
35163     isOriginTop : false,
35164     /**
35165      * @cfg {Boolean} isLayoutInstant = no animation?
35166      */   
35167     isLayoutInstant : false, // needed?
35168     /**
35169      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35170      */   
35171     isResizingContainer : true,
35172     /**
35173      * @cfg {Number} columnWidth  width of the columns 
35174      */   
35175     
35176     columnWidth : 0,
35177     
35178     /**
35179      * @cfg {Number} maxCols maximum number of columns
35180      */   
35181     
35182     maxCols: 0,
35183     /**
35184      * @cfg {Number} padHeight padding below box..
35185      */   
35186     
35187     padHeight : 10, 
35188     
35189     /**
35190      * @cfg {Boolean} isAutoInitial defalut true
35191      */   
35192     
35193     isAutoInitial : true, 
35194     
35195     // private?
35196     gutter : 0,
35197     
35198     containerWidth: 0,
35199     initialColumnWidth : 0,
35200     currentSize : null,
35201     
35202     colYs : null, // array.
35203     maxY : 0,
35204     padWidth: 10,
35205     
35206     
35207     tag: 'div',
35208     cls: '',
35209     bricks: null, //CompositeElement
35210     cols : 0, // array?
35211     // element : null, // wrapped now this.el
35212     _isLayoutInited : null, 
35213     
35214     
35215     getAutoCreate : function(){
35216         
35217         var cfg = {
35218             tag: this.tag,
35219             cls: 'blog-masonary-wrapper ' + this.cls,
35220             cn : {
35221                 cls : 'mas-boxes masonary'
35222             }
35223         };
35224         
35225         return cfg;
35226     },
35227     
35228     getChildContainer: function( )
35229     {
35230         if (this.boxesEl) {
35231             return this.boxesEl;
35232         }
35233         
35234         this.boxesEl = this.el.select('.mas-boxes').first();
35235         
35236         return this.boxesEl;
35237     },
35238     
35239     
35240     initEvents : function()
35241     {
35242         var _this = this;
35243         
35244         if(this.isAutoInitial){
35245             Roo.log('hook children rendered');
35246             this.on('childrenrendered', function() {
35247                 Roo.log('children rendered');
35248                 _this.initial();
35249             } ,this);
35250         }
35251         
35252     },
35253     
35254     initial : function()
35255     {
35256         this.reloadItems();
35257
35258         this.currentSize = this.el.getBox(true);
35259
35260         /// was window resize... - let's see if this works..
35261         Roo.EventManager.onWindowResize(this.resize, this); 
35262
35263         if(!this.isAutoInitial){
35264             this.layout();
35265             return;
35266         }
35267         
35268         this.layout.defer(500,this);
35269     },
35270     
35271     reloadItems: function()
35272     {
35273         this.bricks = this.el.select('.masonry-brick', true);
35274         
35275         this.bricks.each(function(b) {
35276             //Roo.log(b.getSize());
35277             if (!b.attr('originalwidth')) {
35278                 b.attr('originalwidth',  b.getSize().width);
35279             }
35280             
35281         });
35282         
35283         Roo.log(this.bricks.elements.length);
35284     },
35285     
35286     resize : function()
35287     {
35288         Roo.log('resize');
35289         var cs = this.el.getBox(true);
35290         
35291         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35292             Roo.log("no change in with or X");
35293             return;
35294         }
35295         this.currentSize = cs;
35296         this.layout();
35297     },
35298     
35299     layout : function()
35300     {
35301          Roo.log('layout');
35302         this._resetLayout();
35303         //this._manageStamps();
35304       
35305         // don't animate first layout
35306         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35307         this.layoutItems( isInstant );
35308       
35309         // flag for initalized
35310         this._isLayoutInited = true;
35311     },
35312     
35313     layoutItems : function( isInstant )
35314     {
35315         //var items = this._getItemsForLayout( this.items );
35316         // original code supports filtering layout items.. we just ignore it..
35317         
35318         this._layoutItems( this.bricks , isInstant );
35319       
35320         this._postLayout();
35321     },
35322     _layoutItems : function ( items , isInstant)
35323     {
35324        //this.fireEvent( 'layout', this, items );
35325     
35326
35327         if ( !items || !items.elements.length ) {
35328           // no items, emit event with empty array
35329             return;
35330         }
35331
35332         var queue = [];
35333         items.each(function(item) {
35334             Roo.log("layout item");
35335             Roo.log(item);
35336             // get x/y object from method
35337             var position = this._getItemLayoutPosition( item );
35338             // enqueue
35339             position.item = item;
35340             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35341             queue.push( position );
35342         }, this);
35343       
35344         this._processLayoutQueue( queue );
35345     },
35346     /** Sets position of item in DOM
35347     * @param {Element} item
35348     * @param {Number} x - horizontal position
35349     * @param {Number} y - vertical position
35350     * @param {Boolean} isInstant - disables transitions
35351     */
35352     _processLayoutQueue : function( queue )
35353     {
35354         for ( var i=0, len = queue.length; i < len; i++ ) {
35355             var obj = queue[i];
35356             obj.item.position('absolute');
35357             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35358         }
35359     },
35360       
35361     
35362     /**
35363     * Any logic you want to do after each layout,
35364     * i.e. size the container
35365     */
35366     _postLayout : function()
35367     {
35368         this.resizeContainer();
35369     },
35370     
35371     resizeContainer : function()
35372     {
35373         if ( !this.isResizingContainer ) {
35374             return;
35375         }
35376         var size = this._getContainerSize();
35377         if ( size ) {
35378             this.el.setSize(size.width,size.height);
35379             this.boxesEl.setSize(size.width,size.height);
35380         }
35381     },
35382     
35383     
35384     
35385     _resetLayout : function()
35386     {
35387         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35388         this.colWidth = this.el.getWidth();
35389         //this.gutter = this.el.getWidth(); 
35390         
35391         this.measureColumns();
35392
35393         // reset column Y
35394         var i = this.cols;
35395         this.colYs = [];
35396         while (i--) {
35397             this.colYs.push( 0 );
35398         }
35399     
35400         this.maxY = 0;
35401     },
35402
35403     measureColumns : function()
35404     {
35405         this.getContainerWidth();
35406       // if columnWidth is 0, default to outerWidth of first item
35407         if ( !this.columnWidth ) {
35408             var firstItem = this.bricks.first();
35409             Roo.log(firstItem);
35410             this.columnWidth  = this.containerWidth;
35411             if (firstItem && firstItem.attr('originalwidth') ) {
35412                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35413             }
35414             // columnWidth fall back to item of first element
35415             Roo.log("set column width?");
35416                         this.initialColumnWidth = this.columnWidth  ;
35417
35418             // if first elem has no width, default to size of container
35419             
35420         }
35421         
35422         
35423         if (this.initialColumnWidth) {
35424             this.columnWidth = this.initialColumnWidth;
35425         }
35426         
35427         
35428             
35429         // column width is fixed at the top - however if container width get's smaller we should
35430         // reduce it...
35431         
35432         // this bit calcs how man columns..
35433             
35434         var columnWidth = this.columnWidth += this.gutter;
35435       
35436         // calculate columns
35437         var containerWidth = this.containerWidth + this.gutter;
35438         
35439         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35440         // fix rounding errors, typically with gutters
35441         var excess = columnWidth - containerWidth % columnWidth;
35442         
35443         
35444         // if overshoot is less than a pixel, round up, otherwise floor it
35445         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35446         cols = Math[ mathMethod ]( cols );
35447         this.cols = Math.max( cols, 1 );
35448         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35449         
35450          // padding positioning..
35451         var totalColWidth = this.cols * this.columnWidth;
35452         var padavail = this.containerWidth - totalColWidth;
35453         // so for 2 columns - we need 3 'pads'
35454         
35455         var padNeeded = (1+this.cols) * this.padWidth;
35456         
35457         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35458         
35459         this.columnWidth += padExtra
35460         //this.padWidth = Math.floor(padavail /  ( this.cols));
35461         
35462         // adjust colum width so that padding is fixed??
35463         
35464         // we have 3 columns ... total = width * 3
35465         // we have X left over... that should be used by 
35466         
35467         //if (this.expandC) {
35468             
35469         //}
35470         
35471         
35472         
35473     },
35474     
35475     getContainerWidth : function()
35476     {
35477        /* // container is parent if fit width
35478         var container = this.isFitWidth ? this.element.parentNode : this.element;
35479         // check that this.size and size are there
35480         // IE8 triggers resize on body size change, so they might not be
35481         
35482         var size = getSize( container );  //FIXME
35483         this.containerWidth = size && size.innerWidth; //FIXME
35484         */
35485          
35486         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35487         
35488     },
35489     
35490     _getItemLayoutPosition : function( item )  // what is item?
35491     {
35492         // we resize the item to our columnWidth..
35493       
35494         item.setWidth(this.columnWidth);
35495         item.autoBoxAdjust  = false;
35496         
35497         var sz = item.getSize();
35498  
35499         // how many columns does this brick span
35500         var remainder = this.containerWidth % this.columnWidth;
35501         
35502         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35503         // round if off by 1 pixel, otherwise use ceil
35504         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35505         colSpan = Math.min( colSpan, this.cols );
35506         
35507         // normally this should be '1' as we dont' currently allow multi width columns..
35508         
35509         var colGroup = this._getColGroup( colSpan );
35510         // get the minimum Y value from the columns
35511         var minimumY = Math.min.apply( Math, colGroup );
35512         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35513         
35514         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35515          
35516         // position the brick
35517         var position = {
35518             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35519             y: this.currentSize.y + minimumY + this.padHeight
35520         };
35521         
35522         Roo.log(position);
35523         // apply setHeight to necessary columns
35524         var setHeight = minimumY + sz.height + this.padHeight;
35525         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35526         
35527         var setSpan = this.cols + 1 - colGroup.length;
35528         for ( var i = 0; i < setSpan; i++ ) {
35529           this.colYs[ shortColIndex + i ] = setHeight ;
35530         }
35531       
35532         return position;
35533     },
35534     
35535     /**
35536      * @param {Number} colSpan - number of columns the element spans
35537      * @returns {Array} colGroup
35538      */
35539     _getColGroup : function( colSpan )
35540     {
35541         if ( colSpan < 2 ) {
35542           // if brick spans only one column, use all the column Ys
35543           return this.colYs;
35544         }
35545       
35546         var colGroup = [];
35547         // how many different places could this brick fit horizontally
35548         var groupCount = this.cols + 1 - colSpan;
35549         // for each group potential horizontal position
35550         for ( var i = 0; i < groupCount; i++ ) {
35551           // make an array of colY values for that one group
35552           var groupColYs = this.colYs.slice( i, i + colSpan );
35553           // and get the max value of the array
35554           colGroup[i] = Math.max.apply( Math, groupColYs );
35555         }
35556         return colGroup;
35557     },
35558     /*
35559     _manageStamp : function( stamp )
35560     {
35561         var stampSize =  stamp.getSize();
35562         var offset = stamp.getBox();
35563         // get the columns that this stamp affects
35564         var firstX = this.isOriginLeft ? offset.x : offset.right;
35565         var lastX = firstX + stampSize.width;
35566         var firstCol = Math.floor( firstX / this.columnWidth );
35567         firstCol = Math.max( 0, firstCol );
35568         
35569         var lastCol = Math.floor( lastX / this.columnWidth );
35570         // lastCol should not go over if multiple of columnWidth #425
35571         lastCol -= lastX % this.columnWidth ? 0 : 1;
35572         lastCol = Math.min( this.cols - 1, lastCol );
35573         
35574         // set colYs to bottom of the stamp
35575         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35576             stampSize.height;
35577             
35578         for ( var i = firstCol; i <= lastCol; i++ ) {
35579           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35580         }
35581     },
35582     */
35583     
35584     _getContainerSize : function()
35585     {
35586         this.maxY = Math.max.apply( Math, this.colYs );
35587         var size = {
35588             height: this.maxY
35589         };
35590       
35591         if ( this.isFitWidth ) {
35592             size.width = this._getContainerFitWidth();
35593         }
35594       
35595         return size;
35596     },
35597     
35598     _getContainerFitWidth : function()
35599     {
35600         var unusedCols = 0;
35601         // count unused columns
35602         var i = this.cols;
35603         while ( --i ) {
35604           if ( this.colYs[i] !== 0 ) {
35605             break;
35606           }
35607           unusedCols++;
35608         }
35609         // fit container to columns that have been used
35610         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35611     },
35612     
35613     needsResizeLayout : function()
35614     {
35615         var previousWidth = this.containerWidth;
35616         this.getContainerWidth();
35617         return previousWidth !== this.containerWidth;
35618     }
35619  
35620 });
35621
35622  
35623
35624  /*
35625  * - LGPL
35626  *
35627  * element
35628  * 
35629  */
35630
35631 /**
35632  * @class Roo.bootstrap.MasonryBrick
35633  * @extends Roo.bootstrap.Component
35634  * Bootstrap MasonryBrick class
35635  * 
35636  * @constructor
35637  * Create a new MasonryBrick
35638  * @param {Object} config The config object
35639  */
35640
35641 Roo.bootstrap.MasonryBrick = function(config){
35642     
35643     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35644     
35645     Roo.bootstrap.MasonryBrick.register(this);
35646     
35647     this.addEvents({
35648         // raw events
35649         /**
35650          * @event click
35651          * When a MasonryBrick is clcik
35652          * @param {Roo.bootstrap.MasonryBrick} this
35653          * @param {Roo.EventObject} e
35654          */
35655         "click" : true
35656     });
35657 };
35658
35659 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35660     
35661     /**
35662      * @cfg {String} title
35663      */   
35664     title : '',
35665     /**
35666      * @cfg {String} html
35667      */   
35668     html : '',
35669     /**
35670      * @cfg {String} bgimage
35671      */   
35672     bgimage : '',
35673     /**
35674      * @cfg {String} videourl
35675      */   
35676     videourl : '',
35677     /**
35678      * @cfg {String} cls
35679      */   
35680     cls : '',
35681     /**
35682      * @cfg {String} href
35683      */   
35684     href : '',
35685     /**
35686      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35687      */   
35688     size : 'xs',
35689     
35690     /**
35691      * @cfg {String} placetitle (center|bottom)
35692      */   
35693     placetitle : '',
35694     
35695     /**
35696      * @cfg {Boolean} isFitContainer defalut true
35697      */   
35698     isFitContainer : true, 
35699     
35700     /**
35701      * @cfg {Boolean} preventDefault defalut false
35702      */   
35703     preventDefault : false, 
35704     
35705     /**
35706      * @cfg {Boolean} inverse defalut false
35707      */   
35708     maskInverse : false, 
35709     
35710     getAutoCreate : function()
35711     {
35712         if(!this.isFitContainer){
35713             return this.getSplitAutoCreate();
35714         }
35715         
35716         var cls = 'masonry-brick masonry-brick-full';
35717         
35718         if(this.href.length){
35719             cls += ' masonry-brick-link';
35720         }
35721         
35722         if(this.bgimage.length){
35723             cls += ' masonry-brick-image';
35724         }
35725         
35726         if(this.maskInverse){
35727             cls += ' mask-inverse';
35728         }
35729         
35730         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35731             cls += ' enable-mask';
35732         }
35733         
35734         if(this.size){
35735             cls += ' masonry-' + this.size + '-brick';
35736         }
35737         
35738         if(this.placetitle.length){
35739             
35740             switch (this.placetitle) {
35741                 case 'center' :
35742                     cls += ' masonry-center-title';
35743                     break;
35744                 case 'bottom' :
35745                     cls += ' masonry-bottom-title';
35746                     break;
35747                 default:
35748                     break;
35749             }
35750             
35751         } else {
35752             if(!this.html.length && !this.bgimage.length){
35753                 cls += ' masonry-center-title';
35754             }
35755
35756             if(!this.html.length && this.bgimage.length){
35757                 cls += ' masonry-bottom-title';
35758             }
35759         }
35760         
35761         if(this.cls){
35762             cls += ' ' + this.cls;
35763         }
35764         
35765         var cfg = {
35766             tag: (this.href.length) ? 'a' : 'div',
35767             cls: cls,
35768             cn: [
35769                 {
35770                     tag: 'div',
35771                     cls: 'masonry-brick-mask'
35772                 },
35773                 {
35774                     tag: 'div',
35775                     cls: 'masonry-brick-paragraph',
35776                     cn: []
35777                 }
35778             ]
35779         };
35780         
35781         if(this.href.length){
35782             cfg.href = this.href;
35783         }
35784         
35785         var cn = cfg.cn[1].cn;
35786         
35787         if(this.title.length){
35788             cn.push({
35789                 tag: 'h4',
35790                 cls: 'masonry-brick-title',
35791                 html: this.title
35792             });
35793         }
35794         
35795         if(this.html.length){
35796             cn.push({
35797                 tag: 'p',
35798                 cls: 'masonry-brick-text',
35799                 html: this.html
35800             });
35801         }
35802         
35803         if (!this.title.length && !this.html.length) {
35804             cfg.cn[1].cls += ' hide';
35805         }
35806         
35807         if(this.bgimage.length){
35808             cfg.cn.push({
35809                 tag: 'img',
35810                 cls: 'masonry-brick-image-view',
35811                 src: this.bgimage
35812             });
35813         }
35814         
35815         if(this.videourl.length){
35816             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35817             // youtube support only?
35818             cfg.cn.push({
35819                 tag: 'iframe',
35820                 cls: 'masonry-brick-image-view',
35821                 src: vurl,
35822                 frameborder : 0,
35823                 allowfullscreen : true
35824             });
35825         }
35826         
35827         return cfg;
35828         
35829     },
35830     
35831     getSplitAutoCreate : function()
35832     {
35833         var cls = 'masonry-brick masonry-brick-split';
35834         
35835         if(this.href.length){
35836             cls += ' masonry-brick-link';
35837         }
35838         
35839         if(this.bgimage.length){
35840             cls += ' masonry-brick-image';
35841         }
35842         
35843         if(this.size){
35844             cls += ' masonry-' + this.size + '-brick';
35845         }
35846         
35847         switch (this.placetitle) {
35848             case 'center' :
35849                 cls += ' masonry-center-title';
35850                 break;
35851             case 'bottom' :
35852                 cls += ' masonry-bottom-title';
35853                 break;
35854             default:
35855                 if(!this.bgimage.length){
35856                     cls += ' masonry-center-title';
35857                 }
35858
35859                 if(this.bgimage.length){
35860                     cls += ' masonry-bottom-title';
35861                 }
35862                 break;
35863         }
35864         
35865         if(this.cls){
35866             cls += ' ' + this.cls;
35867         }
35868         
35869         var cfg = {
35870             tag: (this.href.length) ? 'a' : 'div',
35871             cls: cls,
35872             cn: [
35873                 {
35874                     tag: 'div',
35875                     cls: 'masonry-brick-split-head',
35876                     cn: [
35877                         {
35878                             tag: 'div',
35879                             cls: 'masonry-brick-paragraph',
35880                             cn: []
35881                         }
35882                     ]
35883                 },
35884                 {
35885                     tag: 'div',
35886                     cls: 'masonry-brick-split-body',
35887                     cn: []
35888                 }
35889             ]
35890         };
35891         
35892         if(this.href.length){
35893             cfg.href = this.href;
35894         }
35895         
35896         if(this.title.length){
35897             cfg.cn[0].cn[0].cn.push({
35898                 tag: 'h4',
35899                 cls: 'masonry-brick-title',
35900                 html: this.title
35901             });
35902         }
35903         
35904         if(this.html.length){
35905             cfg.cn[1].cn.push({
35906                 tag: 'p',
35907                 cls: 'masonry-brick-text',
35908                 html: this.html
35909             });
35910         }
35911
35912         if(this.bgimage.length){
35913             cfg.cn[0].cn.push({
35914                 tag: 'img',
35915                 cls: 'masonry-brick-image-view',
35916                 src: this.bgimage
35917             });
35918         }
35919         
35920         if(this.videourl.length){
35921             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35922             // youtube support only?
35923             cfg.cn[0].cn.cn.push({
35924                 tag: 'iframe',
35925                 cls: 'masonry-brick-image-view',
35926                 src: vurl,
35927                 frameborder : 0,
35928                 allowfullscreen : true
35929             });
35930         }
35931         
35932         return cfg;
35933     },
35934     
35935     initEvents: function() 
35936     {
35937         switch (this.size) {
35938             case 'xs' :
35939                 this.x = 1;
35940                 this.y = 1;
35941                 break;
35942             case 'sm' :
35943                 this.x = 2;
35944                 this.y = 2;
35945                 break;
35946             case 'md' :
35947             case 'md-left' :
35948             case 'md-right' :
35949                 this.x = 3;
35950                 this.y = 3;
35951                 break;
35952             case 'tall' :
35953                 this.x = 2;
35954                 this.y = 3;
35955                 break;
35956             case 'wide' :
35957                 this.x = 3;
35958                 this.y = 2;
35959                 break;
35960             case 'wide-thin' :
35961                 this.x = 3;
35962                 this.y = 1;
35963                 break;
35964                         
35965             default :
35966                 break;
35967         }
35968         
35969         if(Roo.isTouch){
35970             this.el.on('touchstart', this.onTouchStart, this);
35971             this.el.on('touchmove', this.onTouchMove, this);
35972             this.el.on('touchend', this.onTouchEnd, this);
35973             this.el.on('contextmenu', this.onContextMenu, this);
35974         } else {
35975             this.el.on('mouseenter'  ,this.enter, this);
35976             this.el.on('mouseleave', this.leave, this);
35977             this.el.on('click', this.onClick, this);
35978         }
35979         
35980         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35981             this.parent().bricks.push(this);   
35982         }
35983         
35984     },
35985     
35986     onClick: function(e, el)
35987     {
35988         var time = this.endTimer - this.startTimer;
35989         // Roo.log(e.preventDefault());
35990         if(Roo.isTouch){
35991             if(time > 1000){
35992                 e.preventDefault();
35993                 return;
35994             }
35995         }
35996         
35997         if(!this.preventDefault){
35998             return;
35999         }
36000         
36001         e.preventDefault();
36002         
36003         if (this.activeClass != '') {
36004             this.selectBrick();
36005         }
36006         
36007         this.fireEvent('click', this, e);
36008     },
36009     
36010     enter: function(e, el)
36011     {
36012         e.preventDefault();
36013         
36014         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36015             return;
36016         }
36017         
36018         if(this.bgimage.length && this.html.length){
36019             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36020         }
36021     },
36022     
36023     leave: function(e, el)
36024     {
36025         e.preventDefault();
36026         
36027         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36028             return;
36029         }
36030         
36031         if(this.bgimage.length && this.html.length){
36032             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36033         }
36034     },
36035     
36036     onTouchStart: function(e, el)
36037     {
36038 //        e.preventDefault();
36039         
36040         this.touchmoved = false;
36041         
36042         if(!this.isFitContainer){
36043             return;
36044         }
36045         
36046         if(!this.bgimage.length || !this.html.length){
36047             return;
36048         }
36049         
36050         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36051         
36052         this.timer = new Date().getTime();
36053         
36054     },
36055     
36056     onTouchMove: function(e, el)
36057     {
36058         this.touchmoved = true;
36059     },
36060     
36061     onContextMenu : function(e,el)
36062     {
36063         e.preventDefault();
36064         e.stopPropagation();
36065         return false;
36066     },
36067     
36068     onTouchEnd: function(e, el)
36069     {
36070 //        e.preventDefault();
36071         
36072         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36073         
36074             this.leave(e,el);
36075             
36076             return;
36077         }
36078         
36079         if(!this.bgimage.length || !this.html.length){
36080             
36081             if(this.href.length){
36082                 window.location.href = this.href;
36083             }
36084             
36085             return;
36086         }
36087         
36088         if(!this.isFitContainer){
36089             return;
36090         }
36091         
36092         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36093         
36094         window.location.href = this.href;
36095     },
36096     
36097     //selection on single brick only
36098     selectBrick : function() {
36099         
36100         if (!this.parentId) {
36101             return;
36102         }
36103         
36104         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36105         var index = m.selectedBrick.indexOf(this.id);
36106         
36107         if ( index > -1) {
36108             m.selectedBrick.splice(index,1);
36109             this.el.removeClass(this.activeClass);
36110             return;
36111         }
36112         
36113         for(var i = 0; i < m.selectedBrick.length; i++) {
36114             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36115             b.el.removeClass(b.activeClass);
36116         }
36117         
36118         m.selectedBrick = [];
36119         
36120         m.selectedBrick.push(this.id);
36121         this.el.addClass(this.activeClass);
36122         return;
36123     },
36124     
36125     isSelected : function(){
36126         return this.el.hasClass(this.activeClass);
36127         
36128     }
36129 });
36130
36131 Roo.apply(Roo.bootstrap.MasonryBrick, {
36132     
36133     //groups: {},
36134     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36135      /**
36136     * register a Masonry Brick
36137     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36138     */
36139     
36140     register : function(brick)
36141     {
36142         //this.groups[brick.id] = brick;
36143         this.groups.add(brick.id, brick);
36144     },
36145     /**
36146     * fetch a  masonry brick based on the masonry brick ID
36147     * @param {string} the masonry brick to add
36148     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36149     */
36150     
36151     get: function(brick_id) 
36152     {
36153         // if (typeof(this.groups[brick_id]) == 'undefined') {
36154         //     return false;
36155         // }
36156         // return this.groups[brick_id] ;
36157         
36158         if(this.groups.key(brick_id)) {
36159             return this.groups.key(brick_id);
36160         }
36161         
36162         return false;
36163     }
36164     
36165     
36166     
36167 });
36168
36169  /*
36170  * - LGPL
36171  *
36172  * element
36173  * 
36174  */
36175
36176 /**
36177  * @class Roo.bootstrap.Brick
36178  * @extends Roo.bootstrap.Component
36179  * Bootstrap Brick class
36180  * 
36181  * @constructor
36182  * Create a new Brick
36183  * @param {Object} config The config object
36184  */
36185
36186 Roo.bootstrap.Brick = function(config){
36187     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36188     
36189     this.addEvents({
36190         // raw events
36191         /**
36192          * @event click
36193          * When a Brick is click
36194          * @param {Roo.bootstrap.Brick} this
36195          * @param {Roo.EventObject} e
36196          */
36197         "click" : true
36198     });
36199 };
36200
36201 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36202     
36203     /**
36204      * @cfg {String} title
36205      */   
36206     title : '',
36207     /**
36208      * @cfg {String} html
36209      */   
36210     html : '',
36211     /**
36212      * @cfg {String} bgimage
36213      */   
36214     bgimage : '',
36215     /**
36216      * @cfg {String} cls
36217      */   
36218     cls : '',
36219     /**
36220      * @cfg {String} href
36221      */   
36222     href : '',
36223     /**
36224      * @cfg {String} video
36225      */   
36226     video : '',
36227     /**
36228      * @cfg {Boolean} square
36229      */   
36230     square : true,
36231     
36232     getAutoCreate : function()
36233     {
36234         var cls = 'roo-brick';
36235         
36236         if(this.href.length){
36237             cls += ' roo-brick-link';
36238         }
36239         
36240         if(this.bgimage.length){
36241             cls += ' roo-brick-image';
36242         }
36243         
36244         if(!this.html.length && !this.bgimage.length){
36245             cls += ' roo-brick-center-title';
36246         }
36247         
36248         if(!this.html.length && this.bgimage.length){
36249             cls += ' roo-brick-bottom-title';
36250         }
36251         
36252         if(this.cls){
36253             cls += ' ' + this.cls;
36254         }
36255         
36256         var cfg = {
36257             tag: (this.href.length) ? 'a' : 'div',
36258             cls: cls,
36259             cn: [
36260                 {
36261                     tag: 'div',
36262                     cls: 'roo-brick-paragraph',
36263                     cn: []
36264                 }
36265             ]
36266         };
36267         
36268         if(this.href.length){
36269             cfg.href = this.href;
36270         }
36271         
36272         var cn = cfg.cn[0].cn;
36273         
36274         if(this.title.length){
36275             cn.push({
36276                 tag: 'h4',
36277                 cls: 'roo-brick-title',
36278                 html: this.title
36279             });
36280         }
36281         
36282         if(this.html.length){
36283             cn.push({
36284                 tag: 'p',
36285                 cls: 'roo-brick-text',
36286                 html: this.html
36287             });
36288         } else {
36289             cn.cls += ' hide';
36290         }
36291         
36292         if(this.bgimage.length){
36293             cfg.cn.push({
36294                 tag: 'img',
36295                 cls: 'roo-brick-image-view',
36296                 src: this.bgimage
36297             });
36298         }
36299         
36300         return cfg;
36301     },
36302     
36303     initEvents: function() 
36304     {
36305         if(this.title.length || this.html.length){
36306             this.el.on('mouseenter'  ,this.enter, this);
36307             this.el.on('mouseleave', this.leave, this);
36308         }
36309         
36310         Roo.EventManager.onWindowResize(this.resize, this); 
36311         
36312         if(this.bgimage.length){
36313             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36314             this.imageEl.on('load', this.onImageLoad, this);
36315             return;
36316         }
36317         
36318         this.resize();
36319     },
36320     
36321     onImageLoad : function()
36322     {
36323         this.resize();
36324     },
36325     
36326     resize : function()
36327     {
36328         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36329         
36330         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36331         
36332         if(this.bgimage.length){
36333             var image = this.el.select('.roo-brick-image-view', true).first();
36334             
36335             image.setWidth(paragraph.getWidth());
36336             
36337             if(this.square){
36338                 image.setHeight(paragraph.getWidth());
36339             }
36340             
36341             this.el.setHeight(image.getHeight());
36342             paragraph.setHeight(image.getHeight());
36343             
36344         }
36345         
36346     },
36347     
36348     enter: function(e, el)
36349     {
36350         e.preventDefault();
36351         
36352         if(this.bgimage.length){
36353             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36354             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36355         }
36356     },
36357     
36358     leave: function(e, el)
36359     {
36360         e.preventDefault();
36361         
36362         if(this.bgimage.length){
36363             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36364             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36365         }
36366     }
36367     
36368 });
36369
36370  
36371
36372  /*
36373  * - LGPL
36374  *
36375  * Number field 
36376  */
36377
36378 /**
36379  * @class Roo.bootstrap.NumberField
36380  * @extends Roo.bootstrap.Input
36381  * Bootstrap NumberField class
36382  * 
36383  * 
36384  * 
36385  * 
36386  * @constructor
36387  * Create a new NumberField
36388  * @param {Object} config The config object
36389  */
36390
36391 Roo.bootstrap.NumberField = function(config){
36392     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36393 };
36394
36395 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36396     
36397     /**
36398      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36399      */
36400     allowDecimals : true,
36401     /**
36402      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36403      */
36404     decimalSeparator : ".",
36405     /**
36406      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36407      */
36408     decimalPrecision : 2,
36409     /**
36410      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36411      */
36412     allowNegative : true,
36413     
36414     /**
36415      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36416      */
36417     allowZero: true,
36418     /**
36419      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36420      */
36421     minValue : Number.NEGATIVE_INFINITY,
36422     /**
36423      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36424      */
36425     maxValue : Number.MAX_VALUE,
36426     /**
36427      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36428      */
36429     minText : "The minimum value for this field is {0}",
36430     /**
36431      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36432      */
36433     maxText : "The maximum value for this field is {0}",
36434     /**
36435      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36436      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36437      */
36438     nanText : "{0} is not a valid number",
36439     /**
36440      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36441      */
36442     thousandsDelimiter : false,
36443     /**
36444      * @cfg {String} valueAlign alignment of value
36445      */
36446     valueAlign : "left",
36447
36448     getAutoCreate : function()
36449     {
36450         var hiddenInput = {
36451             tag: 'input',
36452             type: 'hidden',
36453             id: Roo.id(),
36454             cls: 'hidden-number-input'
36455         };
36456         
36457         if (this.name) {
36458             hiddenInput.name = this.name;
36459         }
36460         
36461         this.name = '';
36462         
36463         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36464         
36465         this.name = hiddenInput.name;
36466         
36467         if(cfg.cn.length > 0) {
36468             cfg.cn.push(hiddenInput);
36469         }
36470         
36471         return cfg;
36472     },
36473
36474     // private
36475     initEvents : function()
36476     {   
36477         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36478         
36479         var allowed = "0123456789";
36480         
36481         if(this.allowDecimals){
36482             allowed += this.decimalSeparator;
36483         }
36484         
36485         if(this.allowNegative){
36486             allowed += "-";
36487         }
36488         
36489         if(this.thousandsDelimiter) {
36490             allowed += ",";
36491         }
36492         
36493         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36494         
36495         var keyPress = function(e){
36496             
36497             var k = e.getKey();
36498             
36499             var c = e.getCharCode();
36500             
36501             if(
36502                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36503                     allowed.indexOf(String.fromCharCode(c)) === -1
36504             ){
36505                 e.stopEvent();
36506                 return;
36507             }
36508             
36509             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36510                 return;
36511             }
36512             
36513             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36514                 e.stopEvent();
36515             }
36516         };
36517         
36518         this.el.on("keypress", keyPress, this);
36519     },
36520     
36521     validateValue : function(value)
36522     {
36523         
36524         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36525             return false;
36526         }
36527         
36528         var num = this.parseValue(value);
36529         
36530         if(isNaN(num)){
36531             this.markInvalid(String.format(this.nanText, value));
36532             return false;
36533         }
36534         
36535         if(num < this.minValue){
36536             this.markInvalid(String.format(this.minText, this.minValue));
36537             return false;
36538         }
36539         
36540         if(num > this.maxValue){
36541             this.markInvalid(String.format(this.maxText, this.maxValue));
36542             return false;
36543         }
36544         
36545         return true;
36546     },
36547
36548     getValue : function()
36549     {
36550         var v = this.hiddenEl().getValue();
36551         
36552         return this.fixPrecision(this.parseValue(v));
36553     },
36554
36555     parseValue : function(value)
36556     {
36557         if(this.thousandsDelimiter) {
36558             value += "";
36559             r = new RegExp(",", "g");
36560             value = value.replace(r, "");
36561         }
36562         
36563         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36564         return isNaN(value) ? '' : value;
36565     },
36566
36567     fixPrecision : function(value)
36568     {
36569         if(this.thousandsDelimiter) {
36570             value += "";
36571             r = new RegExp(",", "g");
36572             value = value.replace(r, "");
36573         }
36574         
36575         var nan = isNaN(value);
36576         
36577         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36578             return nan ? '' : value;
36579         }
36580         return parseFloat(value).toFixed(this.decimalPrecision);
36581     },
36582
36583     setValue : function(v)
36584     {
36585         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36586         
36587         this.value = v;
36588         
36589         if(this.rendered){
36590             
36591             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36592             
36593             this.inputEl().dom.value = (v == '') ? '' :
36594                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36595             
36596             if(!this.allowZero && v === '0') {
36597                 this.hiddenEl().dom.value = '';
36598                 this.inputEl().dom.value = '';
36599             }
36600             
36601             this.validate();
36602         }
36603     },
36604
36605     decimalPrecisionFcn : function(v)
36606     {
36607         return Math.floor(v);
36608     },
36609
36610     beforeBlur : function()
36611     {
36612         var v = this.parseValue(this.getRawValue());
36613         
36614         if(v || v === 0 || v === ''){
36615             this.setValue(v);
36616         }
36617     },
36618     
36619     hiddenEl : function()
36620     {
36621         return this.el.select('input.hidden-number-input',true).first();
36622     }
36623     
36624 });
36625
36626  
36627
36628 /*
36629 * Licence: LGPL
36630 */
36631
36632 /**
36633  * @class Roo.bootstrap.DocumentSlider
36634  * @extends Roo.bootstrap.Component
36635  * Bootstrap DocumentSlider class
36636  * 
36637  * @constructor
36638  * Create a new DocumentViewer
36639  * @param {Object} config The config object
36640  */
36641
36642 Roo.bootstrap.DocumentSlider = function(config){
36643     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36644     
36645     this.files = [];
36646     
36647     this.addEvents({
36648         /**
36649          * @event initial
36650          * Fire after initEvent
36651          * @param {Roo.bootstrap.DocumentSlider} this
36652          */
36653         "initial" : true,
36654         /**
36655          * @event update
36656          * Fire after update
36657          * @param {Roo.bootstrap.DocumentSlider} this
36658          */
36659         "update" : true,
36660         /**
36661          * @event click
36662          * Fire after click
36663          * @param {Roo.bootstrap.DocumentSlider} this
36664          */
36665         "click" : true
36666     });
36667 };
36668
36669 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36670     
36671     files : false,
36672     
36673     indicator : 0,
36674     
36675     getAutoCreate : function()
36676     {
36677         var cfg = {
36678             tag : 'div',
36679             cls : 'roo-document-slider',
36680             cn : [
36681                 {
36682                     tag : 'div',
36683                     cls : 'roo-document-slider-header',
36684                     cn : [
36685                         {
36686                             tag : 'div',
36687                             cls : 'roo-document-slider-header-title'
36688                         }
36689                     ]
36690                 },
36691                 {
36692                     tag : 'div',
36693                     cls : 'roo-document-slider-body',
36694                     cn : [
36695                         {
36696                             tag : 'div',
36697                             cls : 'roo-document-slider-prev',
36698                             cn : [
36699                                 {
36700                                     tag : 'i',
36701                                     cls : 'fa fa-chevron-left'
36702                                 }
36703                             ]
36704                         },
36705                         {
36706                             tag : 'div',
36707                             cls : 'roo-document-slider-thumb',
36708                             cn : [
36709                                 {
36710                                     tag : 'img',
36711                                     cls : 'roo-document-slider-image'
36712                                 }
36713                             ]
36714                         },
36715                         {
36716                             tag : 'div',
36717                             cls : 'roo-document-slider-next',
36718                             cn : [
36719                                 {
36720                                     tag : 'i',
36721                                     cls : 'fa fa-chevron-right'
36722                                 }
36723                             ]
36724                         }
36725                     ]
36726                 }
36727             ]
36728         };
36729         
36730         return cfg;
36731     },
36732     
36733     initEvents : function()
36734     {
36735         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36736         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36737         
36738         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36739         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36740         
36741         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36742         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36743         
36744         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36745         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36746         
36747         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36748         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36749         
36750         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36751         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36752         
36753         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36754         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36755         
36756         this.thumbEl.on('click', this.onClick, this);
36757         
36758         this.prevIndicator.on('click', this.prev, this);
36759         
36760         this.nextIndicator.on('click', this.next, this);
36761         
36762     },
36763     
36764     initial : function()
36765     {
36766         if(this.files.length){
36767             this.indicator = 1;
36768             this.update()
36769         }
36770         
36771         this.fireEvent('initial', this);
36772     },
36773     
36774     update : function()
36775     {
36776         this.imageEl.attr('src', this.files[this.indicator - 1]);
36777         
36778         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36779         
36780         this.prevIndicator.show();
36781         
36782         if(this.indicator == 1){
36783             this.prevIndicator.hide();
36784         }
36785         
36786         this.nextIndicator.show();
36787         
36788         if(this.indicator == this.files.length){
36789             this.nextIndicator.hide();
36790         }
36791         
36792         this.thumbEl.scrollTo('top');
36793         
36794         this.fireEvent('update', this);
36795     },
36796     
36797     onClick : function(e)
36798     {
36799         e.preventDefault();
36800         
36801         this.fireEvent('click', this);
36802     },
36803     
36804     prev : function(e)
36805     {
36806         e.preventDefault();
36807         
36808         this.indicator = Math.max(1, this.indicator - 1);
36809         
36810         this.update();
36811     },
36812     
36813     next : function(e)
36814     {
36815         e.preventDefault();
36816         
36817         this.indicator = Math.min(this.files.length, this.indicator + 1);
36818         
36819         this.update();
36820     }
36821 });
36822 /*
36823  * - LGPL
36824  *
36825  * RadioSet
36826  *
36827  *
36828  */
36829
36830 /**
36831  * @class Roo.bootstrap.RadioSet
36832  * @extends Roo.bootstrap.Input
36833  * Bootstrap RadioSet class
36834  * @cfg {String} indicatorpos (left|right) default left
36835  * @cfg {Boolean} inline (true|false) inline the element (default true)
36836  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36837  * @constructor
36838  * Create a new RadioSet
36839  * @param {Object} config The config object
36840  */
36841
36842 Roo.bootstrap.RadioSet = function(config){
36843     
36844     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36845     
36846     this.radioes = [];
36847     
36848     Roo.bootstrap.RadioSet.register(this);
36849     
36850     this.addEvents({
36851         /**
36852         * @event check
36853         * Fires when the element is checked or unchecked.
36854         * @param {Roo.bootstrap.RadioSet} this This radio
36855         * @param {Roo.bootstrap.Radio} item The checked item
36856         */
36857        check : true,
36858        /**
36859         * @event click
36860         * Fires when the element is click.
36861         * @param {Roo.bootstrap.RadioSet} this This radio set
36862         * @param {Roo.bootstrap.Radio} item The checked item
36863         * @param {Roo.EventObject} e The event object
36864         */
36865        click : true
36866     });
36867     
36868 };
36869
36870 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36871
36872     radioes : false,
36873     
36874     inline : true,
36875     
36876     weight : '',
36877     
36878     indicatorpos : 'left',
36879     
36880     getAutoCreate : function()
36881     {
36882         var label = {
36883             tag : 'label',
36884             cls : 'roo-radio-set-label',
36885             cn : [
36886                 {
36887                     tag : 'span',
36888                     html : this.fieldLabel
36889                 }
36890             ]
36891         };
36892         if (Roo.bootstrap.version == 3) {
36893             
36894             
36895             if(this.indicatorpos == 'left'){
36896                 label.cn.unshift({
36897                     tag : 'i',
36898                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36899                     tooltip : 'This field is required'
36900                 });
36901             } else {
36902                 label.cn.push({
36903                     tag : 'i',
36904                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36905                     tooltip : 'This field is required'
36906                 });
36907             }
36908         }
36909         var items = {
36910             tag : 'div',
36911             cls : 'roo-radio-set-items'
36912         };
36913         
36914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36915         
36916         if (align === 'left' && this.fieldLabel.length) {
36917             
36918             items = {
36919                 cls : "roo-radio-set-right", 
36920                 cn: [
36921                     items
36922                 ]
36923             };
36924             
36925             if(this.labelWidth > 12){
36926                 label.style = "width: " + this.labelWidth + 'px';
36927             }
36928             
36929             if(this.labelWidth < 13 && this.labelmd == 0){
36930                 this.labelmd = this.labelWidth;
36931             }
36932             
36933             if(this.labellg > 0){
36934                 label.cls += ' col-lg-' + this.labellg;
36935                 items.cls += ' col-lg-' + (12 - this.labellg);
36936             }
36937             
36938             if(this.labelmd > 0){
36939                 label.cls += ' col-md-' + this.labelmd;
36940                 items.cls += ' col-md-' + (12 - this.labelmd);
36941             }
36942             
36943             if(this.labelsm > 0){
36944                 label.cls += ' col-sm-' + this.labelsm;
36945                 items.cls += ' col-sm-' + (12 - this.labelsm);
36946             }
36947             
36948             if(this.labelxs > 0){
36949                 label.cls += ' col-xs-' + this.labelxs;
36950                 items.cls += ' col-xs-' + (12 - this.labelxs);
36951             }
36952         }
36953         
36954         var cfg = {
36955             tag : 'div',
36956             cls : 'roo-radio-set',
36957             cn : [
36958                 {
36959                     tag : 'input',
36960                     cls : 'roo-radio-set-input',
36961                     type : 'hidden',
36962                     name : this.name,
36963                     value : this.value ? this.value :  ''
36964                 },
36965                 label,
36966                 items
36967             ]
36968         };
36969         
36970         if(this.weight.length){
36971             cfg.cls += ' roo-radio-' + this.weight;
36972         }
36973         
36974         if(this.inline) {
36975             cfg.cls += ' roo-radio-set-inline';
36976         }
36977         
36978         var settings=this;
36979         ['xs','sm','md','lg'].map(function(size){
36980             if (settings[size]) {
36981                 cfg.cls += ' col-' + size + '-' + settings[size];
36982             }
36983         });
36984         
36985         return cfg;
36986         
36987     },
36988
36989     initEvents : function()
36990     {
36991         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36992         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36993         
36994         if(!this.fieldLabel.length){
36995             this.labelEl.hide();
36996         }
36997         
36998         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36999         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37000         
37001         this.indicator = this.indicatorEl();
37002         
37003         if(this.indicator){
37004             this.indicator.addClass('invisible');
37005         }
37006         
37007         this.originalValue = this.getValue();
37008         
37009     },
37010     
37011     inputEl: function ()
37012     {
37013         return this.el.select('.roo-radio-set-input', true).first();
37014     },
37015     
37016     getChildContainer : function()
37017     {
37018         return this.itemsEl;
37019     },
37020     
37021     register : function(item)
37022     {
37023         this.radioes.push(item);
37024         
37025     },
37026     
37027     validate : function()
37028     {   
37029         if(this.getVisibilityEl().hasClass('hidden')){
37030             return true;
37031         }
37032         
37033         var valid = false;
37034         
37035         Roo.each(this.radioes, function(i){
37036             if(!i.checked){
37037                 return;
37038             }
37039             
37040             valid = true;
37041             return false;
37042         });
37043         
37044         if(this.allowBlank) {
37045             return true;
37046         }
37047         
37048         if(this.disabled || valid){
37049             this.markValid();
37050             return true;
37051         }
37052         
37053         this.markInvalid();
37054         return false;
37055         
37056     },
37057     
37058     markValid : function()
37059     {
37060         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37061             this.indicatorEl().removeClass('visible');
37062             this.indicatorEl().addClass('invisible');
37063         }
37064         
37065         
37066         if (Roo.bootstrap.version == 3) {
37067             this.el.removeClass([this.invalidClass, this.validClass]);
37068             this.el.addClass(this.validClass);
37069         } else {
37070             this.el.removeClass(['is-invalid','is-valid']);
37071             this.el.addClass(['is-valid']);
37072         }
37073         this.fireEvent('valid', this);
37074     },
37075     
37076     markInvalid : function(msg)
37077     {
37078         if(this.allowBlank || this.disabled){
37079             return;
37080         }
37081         
37082         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37083             this.indicatorEl().removeClass('invisible');
37084             this.indicatorEl().addClass('visible');
37085         }
37086         if (Roo.bootstrap.version == 3) {
37087             this.el.removeClass([this.invalidClass, this.validClass]);
37088             this.el.addClass(this.invalidClass);
37089         } else {
37090             this.el.removeClass(['is-invalid','is-valid']);
37091             this.el.addClass(['is-invalid']);
37092         }
37093         
37094         this.fireEvent('invalid', this, msg);
37095         
37096     },
37097     
37098     setValue : function(v, suppressEvent)
37099     {   
37100         if(this.value === v){
37101             return;
37102         }
37103         
37104         this.value = v;
37105         
37106         if(this.rendered){
37107             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37108         }
37109         
37110         Roo.each(this.radioes, function(i){
37111             i.checked = false;
37112             i.el.removeClass('checked');
37113         });
37114         
37115         Roo.each(this.radioes, function(i){
37116             
37117             if(i.value === v || i.value.toString() === v.toString()){
37118                 i.checked = true;
37119                 i.el.addClass('checked');
37120                 
37121                 if(suppressEvent !== true){
37122                     this.fireEvent('check', this, i);
37123                 }
37124                 
37125                 return false;
37126             }
37127             
37128         }, this);
37129         
37130         this.validate();
37131     },
37132     
37133     clearInvalid : function(){
37134         
37135         if(!this.el || this.preventMark){
37136             return;
37137         }
37138         
37139         this.el.removeClass([this.invalidClass]);
37140         
37141         this.fireEvent('valid', this);
37142     }
37143     
37144 });
37145
37146 Roo.apply(Roo.bootstrap.RadioSet, {
37147     
37148     groups: {},
37149     
37150     register : function(set)
37151     {
37152         this.groups[set.name] = set;
37153     },
37154     
37155     get: function(name) 
37156     {
37157         if (typeof(this.groups[name]) == 'undefined') {
37158             return false;
37159         }
37160         
37161         return this.groups[name] ;
37162     }
37163     
37164 });
37165 /*
37166  * Based on:
37167  * Ext JS Library 1.1.1
37168  * Copyright(c) 2006-2007, Ext JS, LLC.
37169  *
37170  * Originally Released Under LGPL - original licence link has changed is not relivant.
37171  *
37172  * Fork - LGPL
37173  * <script type="text/javascript">
37174  */
37175
37176
37177 /**
37178  * @class Roo.bootstrap.SplitBar
37179  * @extends Roo.util.Observable
37180  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37181  * <br><br>
37182  * Usage:
37183  * <pre><code>
37184 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37185                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37186 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37187 split.minSize = 100;
37188 split.maxSize = 600;
37189 split.animate = true;
37190 split.on('moved', splitterMoved);
37191 </code></pre>
37192  * @constructor
37193  * Create a new SplitBar
37194  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37195  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37196  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37197  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37198                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37199                         position of the SplitBar).
37200  */
37201 Roo.bootstrap.SplitBar = function(cfg){
37202     
37203     /** @private */
37204     
37205     //{
37206     //  dragElement : elm
37207     //  resizingElement: el,
37208         // optional..
37209     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37210     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37211         // existingProxy ???
37212     //}
37213     
37214     this.el = Roo.get(cfg.dragElement, true);
37215     this.el.dom.unselectable = "on";
37216     /** @private */
37217     this.resizingEl = Roo.get(cfg.resizingElement, true);
37218
37219     /**
37220      * @private
37221      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37222      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37223      * @type Number
37224      */
37225     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37226     
37227     /**
37228      * The minimum size of the resizing element. (Defaults to 0)
37229      * @type Number
37230      */
37231     this.minSize = 0;
37232     
37233     /**
37234      * The maximum size of the resizing element. (Defaults to 2000)
37235      * @type Number
37236      */
37237     this.maxSize = 2000;
37238     
37239     /**
37240      * Whether to animate the transition to the new size
37241      * @type Boolean
37242      */
37243     this.animate = false;
37244     
37245     /**
37246      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37247      * @type Boolean
37248      */
37249     this.useShim = false;
37250     
37251     /** @private */
37252     this.shim = null;
37253     
37254     if(!cfg.existingProxy){
37255         /** @private */
37256         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37257     }else{
37258         this.proxy = Roo.get(cfg.existingProxy).dom;
37259     }
37260     /** @private */
37261     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37262     
37263     /** @private */
37264     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37265     
37266     /** @private */
37267     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37268     
37269     /** @private */
37270     this.dragSpecs = {};
37271     
37272     /**
37273      * @private The adapter to use to positon and resize elements
37274      */
37275     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37276     this.adapter.init(this);
37277     
37278     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37279         /** @private */
37280         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37281         this.el.addClass("roo-splitbar-h");
37282     }else{
37283         /** @private */
37284         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37285         this.el.addClass("roo-splitbar-v");
37286     }
37287     
37288     this.addEvents({
37289         /**
37290          * @event resize
37291          * Fires when the splitter is moved (alias for {@link #event-moved})
37292          * @param {Roo.bootstrap.SplitBar} this
37293          * @param {Number} newSize the new width or height
37294          */
37295         "resize" : true,
37296         /**
37297          * @event moved
37298          * Fires when the splitter is moved
37299          * @param {Roo.bootstrap.SplitBar} this
37300          * @param {Number} newSize the new width or height
37301          */
37302         "moved" : true,
37303         /**
37304          * @event beforeresize
37305          * Fires before the splitter is dragged
37306          * @param {Roo.bootstrap.SplitBar} this
37307          */
37308         "beforeresize" : true,
37309
37310         "beforeapply" : true
37311     });
37312
37313     Roo.util.Observable.call(this);
37314 };
37315
37316 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37317     onStartProxyDrag : function(x, y){
37318         this.fireEvent("beforeresize", this);
37319         if(!this.overlay){
37320             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37321             o.unselectable();
37322             o.enableDisplayMode("block");
37323             // all splitbars share the same overlay
37324             Roo.bootstrap.SplitBar.prototype.overlay = o;
37325         }
37326         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37327         this.overlay.show();
37328         Roo.get(this.proxy).setDisplayed("block");
37329         var size = this.adapter.getElementSize(this);
37330         this.activeMinSize = this.getMinimumSize();;
37331         this.activeMaxSize = this.getMaximumSize();;
37332         var c1 = size - this.activeMinSize;
37333         var c2 = Math.max(this.activeMaxSize - size, 0);
37334         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37335             this.dd.resetConstraints();
37336             this.dd.setXConstraint(
37337                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37338                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37339             );
37340             this.dd.setYConstraint(0, 0);
37341         }else{
37342             this.dd.resetConstraints();
37343             this.dd.setXConstraint(0, 0);
37344             this.dd.setYConstraint(
37345                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37346                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37347             );
37348          }
37349         this.dragSpecs.startSize = size;
37350         this.dragSpecs.startPoint = [x, y];
37351         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37352     },
37353     
37354     /** 
37355      * @private Called after the drag operation by the DDProxy
37356      */
37357     onEndProxyDrag : function(e){
37358         Roo.get(this.proxy).setDisplayed(false);
37359         var endPoint = Roo.lib.Event.getXY(e);
37360         if(this.overlay){
37361             this.overlay.hide();
37362         }
37363         var newSize;
37364         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37365             newSize = this.dragSpecs.startSize + 
37366                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37367                     endPoint[0] - this.dragSpecs.startPoint[0] :
37368                     this.dragSpecs.startPoint[0] - endPoint[0]
37369                 );
37370         }else{
37371             newSize = this.dragSpecs.startSize + 
37372                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37373                     endPoint[1] - this.dragSpecs.startPoint[1] :
37374                     this.dragSpecs.startPoint[1] - endPoint[1]
37375                 );
37376         }
37377         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37378         if(newSize != this.dragSpecs.startSize){
37379             if(this.fireEvent('beforeapply', this, newSize) !== false){
37380                 this.adapter.setElementSize(this, newSize);
37381                 this.fireEvent("moved", this, newSize);
37382                 this.fireEvent("resize", this, newSize);
37383             }
37384         }
37385     },
37386     
37387     /**
37388      * Get the adapter this SplitBar uses
37389      * @return The adapter object
37390      */
37391     getAdapter : function(){
37392         return this.adapter;
37393     },
37394     
37395     /**
37396      * Set the adapter this SplitBar uses
37397      * @param {Object} adapter A SplitBar adapter object
37398      */
37399     setAdapter : function(adapter){
37400         this.adapter = adapter;
37401         this.adapter.init(this);
37402     },
37403     
37404     /**
37405      * Gets the minimum size for the resizing element
37406      * @return {Number} The minimum size
37407      */
37408     getMinimumSize : function(){
37409         return this.minSize;
37410     },
37411     
37412     /**
37413      * Sets the minimum size for the resizing element
37414      * @param {Number} minSize The minimum size
37415      */
37416     setMinimumSize : function(minSize){
37417         this.minSize = minSize;
37418     },
37419     
37420     /**
37421      * Gets the maximum size for the resizing element
37422      * @return {Number} The maximum size
37423      */
37424     getMaximumSize : function(){
37425         return this.maxSize;
37426     },
37427     
37428     /**
37429      * Sets the maximum size for the resizing element
37430      * @param {Number} maxSize The maximum size
37431      */
37432     setMaximumSize : function(maxSize){
37433         this.maxSize = maxSize;
37434     },
37435     
37436     /**
37437      * Sets the initialize size for the resizing element
37438      * @param {Number} size The initial size
37439      */
37440     setCurrentSize : function(size){
37441         var oldAnimate = this.animate;
37442         this.animate = false;
37443         this.adapter.setElementSize(this, size);
37444         this.animate = oldAnimate;
37445     },
37446     
37447     /**
37448      * Destroy this splitbar. 
37449      * @param {Boolean} removeEl True to remove the element
37450      */
37451     destroy : function(removeEl){
37452         if(this.shim){
37453             this.shim.remove();
37454         }
37455         this.dd.unreg();
37456         this.proxy.parentNode.removeChild(this.proxy);
37457         if(removeEl){
37458             this.el.remove();
37459         }
37460     }
37461 });
37462
37463 /**
37464  * @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.
37465  */
37466 Roo.bootstrap.SplitBar.createProxy = function(dir){
37467     var proxy = new Roo.Element(document.createElement("div"));
37468     proxy.unselectable();
37469     var cls = 'roo-splitbar-proxy';
37470     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37471     document.body.appendChild(proxy.dom);
37472     return proxy.dom;
37473 };
37474
37475 /** 
37476  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37477  * Default Adapter. It assumes the splitter and resizing element are not positioned
37478  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37479  */
37480 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37481 };
37482
37483 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37484     // do nothing for now
37485     init : function(s){
37486     
37487     },
37488     /**
37489      * Called before drag operations to get the current size of the resizing element. 
37490      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37491      */
37492      getElementSize : function(s){
37493         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37494             return s.resizingEl.getWidth();
37495         }else{
37496             return s.resizingEl.getHeight();
37497         }
37498     },
37499     
37500     /**
37501      * Called after drag operations to set the size of the resizing element.
37502      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37503      * @param {Number} newSize The new size to set
37504      * @param {Function} onComplete A function to be invoked when resizing is complete
37505      */
37506     setElementSize : function(s, newSize, onComplete){
37507         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37508             if(!s.animate){
37509                 s.resizingEl.setWidth(newSize);
37510                 if(onComplete){
37511                     onComplete(s, newSize);
37512                 }
37513             }else{
37514                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37515             }
37516         }else{
37517             
37518             if(!s.animate){
37519                 s.resizingEl.setHeight(newSize);
37520                 if(onComplete){
37521                     onComplete(s, newSize);
37522                 }
37523             }else{
37524                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37525             }
37526         }
37527     }
37528 };
37529
37530 /** 
37531  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37532  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37533  * Adapter that  moves the splitter element to align with the resized sizing element. 
37534  * Used with an absolute positioned SplitBar.
37535  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37536  * document.body, make sure you assign an id to the body element.
37537  */
37538 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37539     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37540     this.container = Roo.get(container);
37541 };
37542
37543 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37544     init : function(s){
37545         this.basic.init(s);
37546     },
37547     
37548     getElementSize : function(s){
37549         return this.basic.getElementSize(s);
37550     },
37551     
37552     setElementSize : function(s, newSize, onComplete){
37553         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37554     },
37555     
37556     moveSplitter : function(s){
37557         var yes = Roo.bootstrap.SplitBar;
37558         switch(s.placement){
37559             case yes.LEFT:
37560                 s.el.setX(s.resizingEl.getRight());
37561                 break;
37562             case yes.RIGHT:
37563                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37564                 break;
37565             case yes.TOP:
37566                 s.el.setY(s.resizingEl.getBottom());
37567                 break;
37568             case yes.BOTTOM:
37569                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37570                 break;
37571         }
37572     }
37573 };
37574
37575 /**
37576  * Orientation constant - Create a vertical SplitBar
37577  * @static
37578  * @type Number
37579  */
37580 Roo.bootstrap.SplitBar.VERTICAL = 1;
37581
37582 /**
37583  * Orientation constant - Create a horizontal SplitBar
37584  * @static
37585  * @type Number
37586  */
37587 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37588
37589 /**
37590  * Placement constant - The resizing element is to the left of the splitter element
37591  * @static
37592  * @type Number
37593  */
37594 Roo.bootstrap.SplitBar.LEFT = 1;
37595
37596 /**
37597  * Placement constant - The resizing element is to the right of the splitter element
37598  * @static
37599  * @type Number
37600  */
37601 Roo.bootstrap.SplitBar.RIGHT = 2;
37602
37603 /**
37604  * Placement constant - The resizing element is positioned above the splitter element
37605  * @static
37606  * @type Number
37607  */
37608 Roo.bootstrap.SplitBar.TOP = 3;
37609
37610 /**
37611  * Placement constant - The resizing element is positioned under splitter element
37612  * @static
37613  * @type Number
37614  */
37615 Roo.bootstrap.SplitBar.BOTTOM = 4;
37616 Roo.namespace("Roo.bootstrap.layout");/*
37617  * Based on:
37618  * Ext JS Library 1.1.1
37619  * Copyright(c) 2006-2007, Ext JS, LLC.
37620  *
37621  * Originally Released Under LGPL - original licence link has changed is not relivant.
37622  *
37623  * Fork - LGPL
37624  * <script type="text/javascript">
37625  */
37626
37627 /**
37628  * @class Roo.bootstrap.layout.Manager
37629  * @extends Roo.bootstrap.Component
37630  * Base class for layout managers.
37631  */
37632 Roo.bootstrap.layout.Manager = function(config)
37633 {
37634     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37635
37636
37637
37638
37639
37640     /** false to disable window resize monitoring @type Boolean */
37641     this.monitorWindowResize = true;
37642     this.regions = {};
37643     this.addEvents({
37644         /**
37645          * @event layout
37646          * Fires when a layout is performed.
37647          * @param {Roo.LayoutManager} this
37648          */
37649         "layout" : true,
37650         /**
37651          * @event regionresized
37652          * Fires when the user resizes a region.
37653          * @param {Roo.LayoutRegion} region The resized region
37654          * @param {Number} newSize The new size (width for east/west, height for north/south)
37655          */
37656         "regionresized" : true,
37657         /**
37658          * @event regioncollapsed
37659          * Fires when a region is collapsed.
37660          * @param {Roo.LayoutRegion} region The collapsed region
37661          */
37662         "regioncollapsed" : true,
37663         /**
37664          * @event regionexpanded
37665          * Fires when a region is expanded.
37666          * @param {Roo.LayoutRegion} region The expanded region
37667          */
37668         "regionexpanded" : true
37669     });
37670     this.updating = false;
37671
37672     if (config.el) {
37673         this.el = Roo.get(config.el);
37674         this.initEvents();
37675     }
37676
37677 };
37678
37679 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37680
37681
37682     regions : null,
37683
37684     monitorWindowResize : true,
37685
37686
37687     updating : false,
37688
37689
37690     onRender : function(ct, position)
37691     {
37692         if(!this.el){
37693             this.el = Roo.get(ct);
37694             this.initEvents();
37695         }
37696         //this.fireEvent('render',this);
37697     },
37698
37699
37700     initEvents: function()
37701     {
37702
37703
37704         // ie scrollbar fix
37705         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37706             document.body.scroll = "no";
37707         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37708             this.el.position('relative');
37709         }
37710         this.id = this.el.id;
37711         this.el.addClass("roo-layout-container");
37712         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37713         if(this.el.dom != document.body ) {
37714             this.el.on('resize', this.layout,this);
37715             this.el.on('show', this.layout,this);
37716         }
37717
37718     },
37719
37720     /**
37721      * Returns true if this layout is currently being updated
37722      * @return {Boolean}
37723      */
37724     isUpdating : function(){
37725         return this.updating;
37726     },
37727
37728     /**
37729      * Suspend the LayoutManager from doing auto-layouts while
37730      * making multiple add or remove calls
37731      */
37732     beginUpdate : function(){
37733         this.updating = true;
37734     },
37735
37736     /**
37737      * Restore auto-layouts and optionally disable the manager from performing a layout
37738      * @param {Boolean} noLayout true to disable a layout update
37739      */
37740     endUpdate : function(noLayout){
37741         this.updating = false;
37742         if(!noLayout){
37743             this.layout();
37744         }
37745     },
37746
37747     layout: function(){
37748         // abstract...
37749     },
37750
37751     onRegionResized : function(region, newSize){
37752         this.fireEvent("regionresized", region, newSize);
37753         this.layout();
37754     },
37755
37756     onRegionCollapsed : function(region){
37757         this.fireEvent("regioncollapsed", region);
37758     },
37759
37760     onRegionExpanded : function(region){
37761         this.fireEvent("regionexpanded", region);
37762     },
37763
37764     /**
37765      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37766      * performs box-model adjustments.
37767      * @return {Object} The size as an object {width: (the width), height: (the height)}
37768      */
37769     getViewSize : function()
37770     {
37771         var size;
37772         if(this.el.dom != document.body){
37773             size = this.el.getSize();
37774         }else{
37775             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37776         }
37777         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37778         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37779         return size;
37780     },
37781
37782     /**
37783      * Returns the Element this layout is bound to.
37784      * @return {Roo.Element}
37785      */
37786     getEl : function(){
37787         return this.el;
37788     },
37789
37790     /**
37791      * Returns the specified region.
37792      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37793      * @return {Roo.LayoutRegion}
37794      */
37795     getRegion : function(target){
37796         return this.regions[target.toLowerCase()];
37797     },
37798
37799     onWindowResize : function(){
37800         if(this.monitorWindowResize){
37801             this.layout();
37802         }
37803     }
37804 });
37805 /*
37806  * Based on:
37807  * Ext JS Library 1.1.1
37808  * Copyright(c) 2006-2007, Ext JS, LLC.
37809  *
37810  * Originally Released Under LGPL - original licence link has changed is not relivant.
37811  *
37812  * Fork - LGPL
37813  * <script type="text/javascript">
37814  */
37815 /**
37816  * @class Roo.bootstrap.layout.Border
37817  * @extends Roo.bootstrap.layout.Manager
37818  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37819  * please see: examples/bootstrap/nested.html<br><br>
37820  
37821 <b>The container the layout is rendered into can be either the body element or any other element.
37822 If it is not the body element, the container needs to either be an absolute positioned element,
37823 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37824 the container size if it is not the body element.</b>
37825
37826 * @constructor
37827 * Create a new Border
37828 * @param {Object} config Configuration options
37829  */
37830 Roo.bootstrap.layout.Border = function(config){
37831     config = config || {};
37832     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37833     
37834     
37835     
37836     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37837         if(config[region]){
37838             config[region].region = region;
37839             this.addRegion(config[region]);
37840         }
37841     },this);
37842     
37843 };
37844
37845 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37846
37847 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37848     
37849     parent : false, // this might point to a 'nest' or a ???
37850     
37851     /**
37852      * Creates and adds a new region if it doesn't already exist.
37853      * @param {String} target The target region key (north, south, east, west or center).
37854      * @param {Object} config The regions config object
37855      * @return {BorderLayoutRegion} The new region
37856      */
37857     addRegion : function(config)
37858     {
37859         if(!this.regions[config.region]){
37860             var r = this.factory(config);
37861             this.bindRegion(r);
37862         }
37863         return this.regions[config.region];
37864     },
37865
37866     // private (kinda)
37867     bindRegion : function(r){
37868         this.regions[r.config.region] = r;
37869         
37870         r.on("visibilitychange",    this.layout, this);
37871         r.on("paneladded",          this.layout, this);
37872         r.on("panelremoved",        this.layout, this);
37873         r.on("invalidated",         this.layout, this);
37874         r.on("resized",             this.onRegionResized, this);
37875         r.on("collapsed",           this.onRegionCollapsed, this);
37876         r.on("expanded",            this.onRegionExpanded, this);
37877     },
37878
37879     /**
37880      * Performs a layout update.
37881      */
37882     layout : function()
37883     {
37884         if(this.updating) {
37885             return;
37886         }
37887         
37888         // render all the rebions if they have not been done alreayd?
37889         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37890             if(this.regions[region] && !this.regions[region].bodyEl){
37891                 this.regions[region].onRender(this.el)
37892             }
37893         },this);
37894         
37895         var size = this.getViewSize();
37896         var w = size.width;
37897         var h = size.height;
37898         var centerW = w;
37899         var centerH = h;
37900         var centerY = 0;
37901         var centerX = 0;
37902         //var x = 0, y = 0;
37903
37904         var rs = this.regions;
37905         var north = rs["north"];
37906         var south = rs["south"]; 
37907         var west = rs["west"];
37908         var east = rs["east"];
37909         var center = rs["center"];
37910         //if(this.hideOnLayout){ // not supported anymore
37911             //c.el.setStyle("display", "none");
37912         //}
37913         if(north && north.isVisible()){
37914             var b = north.getBox();
37915             var m = north.getMargins();
37916             b.width = w - (m.left+m.right);
37917             b.x = m.left;
37918             b.y = m.top;
37919             centerY = b.height + b.y + m.bottom;
37920             centerH -= centerY;
37921             north.updateBox(this.safeBox(b));
37922         }
37923         if(south && south.isVisible()){
37924             var b = south.getBox();
37925             var m = south.getMargins();
37926             b.width = w - (m.left+m.right);
37927             b.x = m.left;
37928             var totalHeight = (b.height + m.top + m.bottom);
37929             b.y = h - totalHeight + m.top;
37930             centerH -= totalHeight;
37931             south.updateBox(this.safeBox(b));
37932         }
37933         if(west && west.isVisible()){
37934             var b = west.getBox();
37935             var m = west.getMargins();
37936             b.height = centerH - (m.top+m.bottom);
37937             b.x = m.left;
37938             b.y = centerY + m.top;
37939             var totalWidth = (b.width + m.left + m.right);
37940             centerX += totalWidth;
37941             centerW -= totalWidth;
37942             west.updateBox(this.safeBox(b));
37943         }
37944         if(east && east.isVisible()){
37945             var b = east.getBox();
37946             var m = east.getMargins();
37947             b.height = centerH - (m.top+m.bottom);
37948             var totalWidth = (b.width + m.left + m.right);
37949             b.x = w - totalWidth + m.left;
37950             b.y = centerY + m.top;
37951             centerW -= totalWidth;
37952             east.updateBox(this.safeBox(b));
37953         }
37954         if(center){
37955             var m = center.getMargins();
37956             var centerBox = {
37957                 x: centerX + m.left,
37958                 y: centerY + m.top,
37959                 width: centerW - (m.left+m.right),
37960                 height: centerH - (m.top+m.bottom)
37961             };
37962             //if(this.hideOnLayout){
37963                 //center.el.setStyle("display", "block");
37964             //}
37965             center.updateBox(this.safeBox(centerBox));
37966         }
37967         this.el.repaint();
37968         this.fireEvent("layout", this);
37969     },
37970
37971     // private
37972     safeBox : function(box){
37973         box.width = Math.max(0, box.width);
37974         box.height = Math.max(0, box.height);
37975         return box;
37976     },
37977
37978     /**
37979      * Adds a ContentPanel (or subclass) to this layout.
37980      * @param {String} target The target region key (north, south, east, west or center).
37981      * @param {Roo.ContentPanel} panel The panel to add
37982      * @return {Roo.ContentPanel} The added panel
37983      */
37984     add : function(target, panel){
37985          
37986         target = target.toLowerCase();
37987         return this.regions[target].add(panel);
37988     },
37989
37990     /**
37991      * Remove a ContentPanel (or subclass) to this layout.
37992      * @param {String} target The target region key (north, south, east, west or center).
37993      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37994      * @return {Roo.ContentPanel} The removed panel
37995      */
37996     remove : function(target, panel){
37997         target = target.toLowerCase();
37998         return this.regions[target].remove(panel);
37999     },
38000
38001     /**
38002      * Searches all regions for a panel with the specified id
38003      * @param {String} panelId
38004      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38005      */
38006     findPanel : function(panelId){
38007         var rs = this.regions;
38008         for(var target in rs){
38009             if(typeof rs[target] != "function"){
38010                 var p = rs[target].getPanel(panelId);
38011                 if(p){
38012                     return p;
38013                 }
38014             }
38015         }
38016         return null;
38017     },
38018
38019     /**
38020      * Searches all regions for a panel with the specified id and activates (shows) it.
38021      * @param {String/ContentPanel} panelId The panels id or the panel itself
38022      * @return {Roo.ContentPanel} The shown panel or null
38023      */
38024     showPanel : function(panelId) {
38025       var rs = this.regions;
38026       for(var target in rs){
38027          var r = rs[target];
38028          if(typeof r != "function"){
38029             if(r.hasPanel(panelId)){
38030                return r.showPanel(panelId);
38031             }
38032          }
38033       }
38034       return null;
38035    },
38036
38037    /**
38038      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38039      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38040      */
38041    /*
38042     restoreState : function(provider){
38043         if(!provider){
38044             provider = Roo.state.Manager;
38045         }
38046         var sm = new Roo.LayoutStateManager();
38047         sm.init(this, provider);
38048     },
38049 */
38050  
38051  
38052     /**
38053      * Adds a xtype elements to the layout.
38054      * <pre><code>
38055
38056 layout.addxtype({
38057        xtype : 'ContentPanel',
38058        region: 'west',
38059        items: [ .... ]
38060    }
38061 );
38062
38063 layout.addxtype({
38064         xtype : 'NestedLayoutPanel',
38065         region: 'west',
38066         layout: {
38067            center: { },
38068            west: { }   
38069         },
38070         items : [ ... list of content panels or nested layout panels.. ]
38071    }
38072 );
38073 </code></pre>
38074      * @param {Object} cfg Xtype definition of item to add.
38075      */
38076     addxtype : function(cfg)
38077     {
38078         // basically accepts a pannel...
38079         // can accept a layout region..!?!?
38080         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38081         
38082         
38083         // theory?  children can only be panels??
38084         
38085         //if (!cfg.xtype.match(/Panel$/)) {
38086         //    return false;
38087         //}
38088         var ret = false;
38089         
38090         if (typeof(cfg.region) == 'undefined') {
38091             Roo.log("Failed to add Panel, region was not set");
38092             Roo.log(cfg);
38093             return false;
38094         }
38095         var region = cfg.region;
38096         delete cfg.region;
38097         
38098           
38099         var xitems = [];
38100         if (cfg.items) {
38101             xitems = cfg.items;
38102             delete cfg.items;
38103         }
38104         var nb = false;
38105         
38106         if ( region == 'center') {
38107             Roo.log("Center: " + cfg.title);
38108         }
38109         
38110         
38111         switch(cfg.xtype) 
38112         {
38113             case 'Content':  // ContentPanel (el, cfg)
38114             case 'Scroll':  // ContentPanel (el, cfg)
38115             case 'View': 
38116                 cfg.autoCreate = cfg.autoCreate || true;
38117                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38118                 //} else {
38119                 //    var el = this.el.createChild();
38120                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38121                 //}
38122                 
38123                 this.add(region, ret);
38124                 break;
38125             
38126             /*
38127             case 'TreePanel': // our new panel!
38128                 cfg.el = this.el.createChild();
38129                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38130                 this.add(region, ret);
38131                 break;
38132             */
38133             
38134             case 'Nest': 
38135                 // create a new Layout (which is  a Border Layout...
38136                 
38137                 var clayout = cfg.layout;
38138                 clayout.el  = this.el.createChild();
38139                 clayout.items   = clayout.items  || [];
38140                 
38141                 delete cfg.layout;
38142                 
38143                 // replace this exitems with the clayout ones..
38144                 xitems = clayout.items;
38145                  
38146                 // force background off if it's in center...
38147                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38148                     cfg.background = false;
38149                 }
38150                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38151                 
38152                 
38153                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38154                 //console.log('adding nested layout panel '  + cfg.toSource());
38155                 this.add(region, ret);
38156                 nb = {}; /// find first...
38157                 break;
38158             
38159             case 'Grid':
38160                 
38161                 // needs grid and region
38162                 
38163                 //var el = this.getRegion(region).el.createChild();
38164                 /*
38165                  *var el = this.el.createChild();
38166                 // create the grid first...
38167                 cfg.grid.container = el;
38168                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38169                 */
38170                 
38171                 if (region == 'center' && this.active ) {
38172                     cfg.background = false;
38173                 }
38174                 
38175                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38176                 
38177                 this.add(region, ret);
38178                 /*
38179                 if (cfg.background) {
38180                     // render grid on panel activation (if panel background)
38181                     ret.on('activate', function(gp) {
38182                         if (!gp.grid.rendered) {
38183                     //        gp.grid.render(el);
38184                         }
38185                     });
38186                 } else {
38187                   //  cfg.grid.render(el);
38188                 }
38189                 */
38190                 break;
38191            
38192            
38193             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38194                 // it was the old xcomponent building that caused this before.
38195                 // espeically if border is the top element in the tree.
38196                 ret = this;
38197                 break; 
38198                 
38199                     
38200                 
38201                 
38202                 
38203             default:
38204                 /*
38205                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38206                     
38207                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38208                     this.add(region, ret);
38209                 } else {
38210                 */
38211                     Roo.log(cfg);
38212                     throw "Can not add '" + cfg.xtype + "' to Border";
38213                     return null;
38214              
38215                                 
38216              
38217         }
38218         this.beginUpdate();
38219         // add children..
38220         var region = '';
38221         var abn = {};
38222         Roo.each(xitems, function(i)  {
38223             region = nb && i.region ? i.region : false;
38224             
38225             var add = ret.addxtype(i);
38226            
38227             if (region) {
38228                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38229                 if (!i.background) {
38230                     abn[region] = nb[region] ;
38231                 }
38232             }
38233             
38234         });
38235         this.endUpdate();
38236
38237         // make the last non-background panel active..
38238         //if (nb) { Roo.log(abn); }
38239         if (nb) {
38240             
38241             for(var r in abn) {
38242                 region = this.getRegion(r);
38243                 if (region) {
38244                     // tried using nb[r], but it does not work..
38245                      
38246                     region.showPanel(abn[r]);
38247                    
38248                 }
38249             }
38250         }
38251         return ret;
38252         
38253     },
38254     
38255     
38256 // private
38257     factory : function(cfg)
38258     {
38259         
38260         var validRegions = Roo.bootstrap.layout.Border.regions;
38261
38262         var target = cfg.region;
38263         cfg.mgr = this;
38264         
38265         var r = Roo.bootstrap.layout;
38266         Roo.log(target);
38267         switch(target){
38268             case "north":
38269                 return new r.North(cfg);
38270             case "south":
38271                 return new r.South(cfg);
38272             case "east":
38273                 return new r.East(cfg);
38274             case "west":
38275                 return new r.West(cfg);
38276             case "center":
38277                 return new r.Center(cfg);
38278         }
38279         throw 'Layout region "'+target+'" not supported.';
38280     }
38281     
38282     
38283 });
38284  /*
38285  * Based on:
38286  * Ext JS Library 1.1.1
38287  * Copyright(c) 2006-2007, Ext JS, LLC.
38288  *
38289  * Originally Released Under LGPL - original licence link has changed is not relivant.
38290  *
38291  * Fork - LGPL
38292  * <script type="text/javascript">
38293  */
38294  
38295 /**
38296  * @class Roo.bootstrap.layout.Basic
38297  * @extends Roo.util.Observable
38298  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38299  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38300  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38301  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38302  * @cfg {string}   region  the region that it inhabits..
38303  * @cfg {bool}   skipConfig skip config?
38304  * 
38305
38306  */
38307 Roo.bootstrap.layout.Basic = function(config){
38308     
38309     this.mgr = config.mgr;
38310     
38311     this.position = config.region;
38312     
38313     var skipConfig = config.skipConfig;
38314     
38315     this.events = {
38316         /**
38317          * @scope Roo.BasicLayoutRegion
38318          */
38319         
38320         /**
38321          * @event beforeremove
38322          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38323          * @param {Roo.LayoutRegion} this
38324          * @param {Roo.ContentPanel} panel The panel
38325          * @param {Object} e The cancel event object
38326          */
38327         "beforeremove" : true,
38328         /**
38329          * @event invalidated
38330          * Fires when the layout for this region is changed.
38331          * @param {Roo.LayoutRegion} this
38332          */
38333         "invalidated" : true,
38334         /**
38335          * @event visibilitychange
38336          * Fires when this region is shown or hidden 
38337          * @param {Roo.LayoutRegion} this
38338          * @param {Boolean} visibility true or false
38339          */
38340         "visibilitychange" : true,
38341         /**
38342          * @event paneladded
38343          * Fires when a panel is added. 
38344          * @param {Roo.LayoutRegion} this
38345          * @param {Roo.ContentPanel} panel The panel
38346          */
38347         "paneladded" : true,
38348         /**
38349          * @event panelremoved
38350          * Fires when a panel is removed. 
38351          * @param {Roo.LayoutRegion} this
38352          * @param {Roo.ContentPanel} panel The panel
38353          */
38354         "panelremoved" : true,
38355         /**
38356          * @event beforecollapse
38357          * Fires when this region before collapse.
38358          * @param {Roo.LayoutRegion} this
38359          */
38360         "beforecollapse" : true,
38361         /**
38362          * @event collapsed
38363          * Fires when this region is collapsed.
38364          * @param {Roo.LayoutRegion} this
38365          */
38366         "collapsed" : true,
38367         /**
38368          * @event expanded
38369          * Fires when this region is expanded.
38370          * @param {Roo.LayoutRegion} this
38371          */
38372         "expanded" : true,
38373         /**
38374          * @event slideshow
38375          * Fires when this region is slid into view.
38376          * @param {Roo.LayoutRegion} this
38377          */
38378         "slideshow" : true,
38379         /**
38380          * @event slidehide
38381          * Fires when this region slides out of view. 
38382          * @param {Roo.LayoutRegion} this
38383          */
38384         "slidehide" : true,
38385         /**
38386          * @event panelactivated
38387          * Fires when a panel is activated. 
38388          * @param {Roo.LayoutRegion} this
38389          * @param {Roo.ContentPanel} panel The activated panel
38390          */
38391         "panelactivated" : true,
38392         /**
38393          * @event resized
38394          * Fires when the user resizes this region. 
38395          * @param {Roo.LayoutRegion} this
38396          * @param {Number} newSize The new size (width for east/west, height for north/south)
38397          */
38398         "resized" : true
38399     };
38400     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38401     this.panels = new Roo.util.MixedCollection();
38402     this.panels.getKey = this.getPanelId.createDelegate(this);
38403     this.box = null;
38404     this.activePanel = null;
38405     // ensure listeners are added...
38406     
38407     if (config.listeners || config.events) {
38408         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38409             listeners : config.listeners || {},
38410             events : config.events || {}
38411         });
38412     }
38413     
38414     if(skipConfig !== true){
38415         this.applyConfig(config);
38416     }
38417 };
38418
38419 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38420 {
38421     getPanelId : function(p){
38422         return p.getId();
38423     },
38424     
38425     applyConfig : function(config){
38426         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38427         this.config = config;
38428         
38429     },
38430     
38431     /**
38432      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38433      * the width, for horizontal (north, south) the height.
38434      * @param {Number} newSize The new width or height
38435      */
38436     resizeTo : function(newSize){
38437         var el = this.el ? this.el :
38438                  (this.activePanel ? this.activePanel.getEl() : null);
38439         if(el){
38440             switch(this.position){
38441                 case "east":
38442                 case "west":
38443                     el.setWidth(newSize);
38444                     this.fireEvent("resized", this, newSize);
38445                 break;
38446                 case "north":
38447                 case "south":
38448                     el.setHeight(newSize);
38449                     this.fireEvent("resized", this, newSize);
38450                 break;                
38451             }
38452         }
38453     },
38454     
38455     getBox : function(){
38456         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38457     },
38458     
38459     getMargins : function(){
38460         return this.margins;
38461     },
38462     
38463     updateBox : function(box){
38464         this.box = box;
38465         var el = this.activePanel.getEl();
38466         el.dom.style.left = box.x + "px";
38467         el.dom.style.top = box.y + "px";
38468         this.activePanel.setSize(box.width, box.height);
38469     },
38470     
38471     /**
38472      * Returns the container element for this region.
38473      * @return {Roo.Element}
38474      */
38475     getEl : function(){
38476         return this.activePanel;
38477     },
38478     
38479     /**
38480      * Returns true if this region is currently visible.
38481      * @return {Boolean}
38482      */
38483     isVisible : function(){
38484         return this.activePanel ? true : false;
38485     },
38486     
38487     setActivePanel : function(panel){
38488         panel = this.getPanel(panel);
38489         if(this.activePanel && this.activePanel != panel){
38490             this.activePanel.setActiveState(false);
38491             this.activePanel.getEl().setLeftTop(-10000,-10000);
38492         }
38493         this.activePanel = panel;
38494         panel.setActiveState(true);
38495         if(this.box){
38496             panel.setSize(this.box.width, this.box.height);
38497         }
38498         this.fireEvent("panelactivated", this, panel);
38499         this.fireEvent("invalidated");
38500     },
38501     
38502     /**
38503      * Show the specified panel.
38504      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38505      * @return {Roo.ContentPanel} The shown panel or null
38506      */
38507     showPanel : function(panel){
38508         panel = this.getPanel(panel);
38509         if(panel){
38510             this.setActivePanel(panel);
38511         }
38512         return panel;
38513     },
38514     
38515     /**
38516      * Get the active panel for this region.
38517      * @return {Roo.ContentPanel} The active panel or null
38518      */
38519     getActivePanel : function(){
38520         return this.activePanel;
38521     },
38522     
38523     /**
38524      * Add the passed ContentPanel(s)
38525      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38526      * @return {Roo.ContentPanel} The panel added (if only one was added)
38527      */
38528     add : function(panel){
38529         if(arguments.length > 1){
38530             for(var i = 0, len = arguments.length; i < len; i++) {
38531                 this.add(arguments[i]);
38532             }
38533             return null;
38534         }
38535         if(this.hasPanel(panel)){
38536             this.showPanel(panel);
38537             return panel;
38538         }
38539         var el = panel.getEl();
38540         if(el.dom.parentNode != this.mgr.el.dom){
38541             this.mgr.el.dom.appendChild(el.dom);
38542         }
38543         if(panel.setRegion){
38544             panel.setRegion(this);
38545         }
38546         this.panels.add(panel);
38547         el.setStyle("position", "absolute");
38548         if(!panel.background){
38549             this.setActivePanel(panel);
38550             if(this.config.initialSize && this.panels.getCount()==1){
38551                 this.resizeTo(this.config.initialSize);
38552             }
38553         }
38554         this.fireEvent("paneladded", this, panel);
38555         return panel;
38556     },
38557     
38558     /**
38559      * Returns true if the panel is in this region.
38560      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38561      * @return {Boolean}
38562      */
38563     hasPanel : function(panel){
38564         if(typeof panel == "object"){ // must be panel obj
38565             panel = panel.getId();
38566         }
38567         return this.getPanel(panel) ? true : false;
38568     },
38569     
38570     /**
38571      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38572      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38573      * @param {Boolean} preservePanel Overrides the config preservePanel option
38574      * @return {Roo.ContentPanel} The panel that was removed
38575      */
38576     remove : function(panel, preservePanel){
38577         panel = this.getPanel(panel);
38578         if(!panel){
38579             return null;
38580         }
38581         var e = {};
38582         this.fireEvent("beforeremove", this, panel, e);
38583         if(e.cancel === true){
38584             return null;
38585         }
38586         var panelId = panel.getId();
38587         this.panels.removeKey(panelId);
38588         return panel;
38589     },
38590     
38591     /**
38592      * Returns the panel specified or null if it's not in this region.
38593      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38594      * @return {Roo.ContentPanel}
38595      */
38596     getPanel : function(id){
38597         if(typeof id == "object"){ // must be panel obj
38598             return id;
38599         }
38600         return this.panels.get(id);
38601     },
38602     
38603     /**
38604      * Returns this regions position (north/south/east/west/center).
38605      * @return {String} 
38606      */
38607     getPosition: function(){
38608         return this.position;    
38609     }
38610 });/*
38611  * Based on:
38612  * Ext JS Library 1.1.1
38613  * Copyright(c) 2006-2007, Ext JS, LLC.
38614  *
38615  * Originally Released Under LGPL - original licence link has changed is not relivant.
38616  *
38617  * Fork - LGPL
38618  * <script type="text/javascript">
38619  */
38620  
38621 /**
38622  * @class Roo.bootstrap.layout.Region
38623  * @extends Roo.bootstrap.layout.Basic
38624  * This class represents a region in a layout manager.
38625  
38626  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38627  * @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})
38628  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38629  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38630  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38631  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38632  * @cfg {String}    title           The title for the region (overrides panel titles)
38633  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38634  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38635  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38636  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38637  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38638  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38639  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38640  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38641  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38642  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38643
38644  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38645  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38646  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38647  * @cfg {Number}    width           For East/West panels
38648  * @cfg {Number}    height          For North/South panels
38649  * @cfg {Boolean}   split           To show the splitter
38650  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38651  * 
38652  * @cfg {string}   cls             Extra CSS classes to add to region
38653  * 
38654  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38655  * @cfg {string}   region  the region that it inhabits..
38656  *
38657
38658  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38659  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38660
38661  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38662  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38663  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38664  */
38665 Roo.bootstrap.layout.Region = function(config)
38666 {
38667     this.applyConfig(config);
38668
38669     var mgr = config.mgr;
38670     var pos = config.region;
38671     config.skipConfig = true;
38672     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38673     
38674     if (mgr.el) {
38675         this.onRender(mgr.el);   
38676     }
38677      
38678     this.visible = true;
38679     this.collapsed = false;
38680     this.unrendered_panels = [];
38681 };
38682
38683 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38684
38685     position: '', // set by wrapper (eg. north/south etc..)
38686     unrendered_panels : null,  // unrendered panels.
38687     
38688     tabPosition : false,
38689     
38690     mgr: false, // points to 'Border'
38691     
38692     
38693     createBody : function(){
38694         /** This region's body element 
38695         * @type Roo.Element */
38696         this.bodyEl = this.el.createChild({
38697                 tag: "div",
38698                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38699         });
38700     },
38701
38702     onRender: function(ctr, pos)
38703     {
38704         var dh = Roo.DomHelper;
38705         /** This region's container element 
38706         * @type Roo.Element */
38707         this.el = dh.append(ctr.dom, {
38708                 tag: "div",
38709                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38710             }, true);
38711         /** This region's title element 
38712         * @type Roo.Element */
38713     
38714         this.titleEl = dh.append(this.el.dom,  {
38715                 tag: "div",
38716                 unselectable: "on",
38717                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38718                 children:[
38719                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38720                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38721                 ]
38722             }, true);
38723         
38724         this.titleEl.enableDisplayMode();
38725         /** This region's title text element 
38726         * @type HTMLElement */
38727         this.titleTextEl = this.titleEl.dom.firstChild;
38728         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38729         /*
38730         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38731         this.closeBtn.enableDisplayMode();
38732         this.closeBtn.on("click", this.closeClicked, this);
38733         this.closeBtn.hide();
38734     */
38735         this.createBody(this.config);
38736         if(this.config.hideWhenEmpty){
38737             this.hide();
38738             this.on("paneladded", this.validateVisibility, this);
38739             this.on("panelremoved", this.validateVisibility, this);
38740         }
38741         if(this.autoScroll){
38742             this.bodyEl.setStyle("overflow", "auto");
38743         }else{
38744             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38745         }
38746         //if(c.titlebar !== false){
38747             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38748                 this.titleEl.hide();
38749             }else{
38750                 this.titleEl.show();
38751                 if(this.config.title){
38752                     this.titleTextEl.innerHTML = this.config.title;
38753                 }
38754             }
38755         //}
38756         if(this.config.collapsed){
38757             this.collapse(true);
38758         }
38759         if(this.config.hidden){
38760             this.hide();
38761         }
38762         
38763         if (this.unrendered_panels && this.unrendered_panels.length) {
38764             for (var i =0;i< this.unrendered_panels.length; i++) {
38765                 this.add(this.unrendered_panels[i]);
38766             }
38767             this.unrendered_panels = null;
38768             
38769         }
38770         
38771     },
38772     
38773     applyConfig : function(c)
38774     {
38775         /*
38776          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38777             var dh = Roo.DomHelper;
38778             if(c.titlebar !== false){
38779                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38780                 this.collapseBtn.on("click", this.collapse, this);
38781                 this.collapseBtn.enableDisplayMode();
38782                 /*
38783                 if(c.showPin === true || this.showPin){
38784                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38785                     this.stickBtn.enableDisplayMode();
38786                     this.stickBtn.on("click", this.expand, this);
38787                     this.stickBtn.hide();
38788                 }
38789                 
38790             }
38791             */
38792             /** This region's collapsed element
38793             * @type Roo.Element */
38794             /*
38795              *
38796             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38797                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38798             ]}, true);
38799             
38800             if(c.floatable !== false){
38801                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38802                this.collapsedEl.on("click", this.collapseClick, this);
38803             }
38804
38805             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38806                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38807                    id: "message", unselectable: "on", style:{"float":"left"}});
38808                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38809              }
38810             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38811             this.expandBtn.on("click", this.expand, this);
38812             
38813         }
38814         
38815         if(this.collapseBtn){
38816             this.collapseBtn.setVisible(c.collapsible == true);
38817         }
38818         
38819         this.cmargins = c.cmargins || this.cmargins ||
38820                          (this.position == "west" || this.position == "east" ?
38821                              {top: 0, left: 2, right:2, bottom: 0} :
38822                              {top: 2, left: 0, right:0, bottom: 2});
38823         */
38824         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38825         
38826         
38827         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38828         
38829         this.autoScroll = c.autoScroll || false;
38830         
38831         
38832        
38833         
38834         this.duration = c.duration || .30;
38835         this.slideDuration = c.slideDuration || .45;
38836         this.config = c;
38837        
38838     },
38839     /**
38840      * Returns true if this region is currently visible.
38841      * @return {Boolean}
38842      */
38843     isVisible : function(){
38844         return this.visible;
38845     },
38846
38847     /**
38848      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38849      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38850      */
38851     //setCollapsedTitle : function(title){
38852     //    title = title || "&#160;";
38853      //   if(this.collapsedTitleTextEl){
38854       //      this.collapsedTitleTextEl.innerHTML = title;
38855        // }
38856     //},
38857
38858     getBox : function(){
38859         var b;
38860       //  if(!this.collapsed){
38861             b = this.el.getBox(false, true);
38862        // }else{
38863           //  b = this.collapsedEl.getBox(false, true);
38864         //}
38865         return b;
38866     },
38867
38868     getMargins : function(){
38869         return this.margins;
38870         //return this.collapsed ? this.cmargins : this.margins;
38871     },
38872 /*
38873     highlight : function(){
38874         this.el.addClass("x-layout-panel-dragover");
38875     },
38876
38877     unhighlight : function(){
38878         this.el.removeClass("x-layout-panel-dragover");
38879     },
38880 */
38881     updateBox : function(box)
38882     {
38883         if (!this.bodyEl) {
38884             return; // not rendered yet..
38885         }
38886         
38887         this.box = box;
38888         if(!this.collapsed){
38889             this.el.dom.style.left = box.x + "px";
38890             this.el.dom.style.top = box.y + "px";
38891             this.updateBody(box.width, box.height);
38892         }else{
38893             this.collapsedEl.dom.style.left = box.x + "px";
38894             this.collapsedEl.dom.style.top = box.y + "px";
38895             this.collapsedEl.setSize(box.width, box.height);
38896         }
38897         if(this.tabs){
38898             this.tabs.autoSizeTabs();
38899         }
38900     },
38901
38902     updateBody : function(w, h)
38903     {
38904         if(w !== null){
38905             this.el.setWidth(w);
38906             w -= this.el.getBorderWidth("rl");
38907             if(this.config.adjustments){
38908                 w += this.config.adjustments[0];
38909             }
38910         }
38911         if(h !== null && h > 0){
38912             this.el.setHeight(h);
38913             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38914             h -= this.el.getBorderWidth("tb");
38915             if(this.config.adjustments){
38916                 h += this.config.adjustments[1];
38917             }
38918             this.bodyEl.setHeight(h);
38919             if(this.tabs){
38920                 h = this.tabs.syncHeight(h);
38921             }
38922         }
38923         if(this.panelSize){
38924             w = w !== null ? w : this.panelSize.width;
38925             h = h !== null ? h : this.panelSize.height;
38926         }
38927         if(this.activePanel){
38928             var el = this.activePanel.getEl();
38929             w = w !== null ? w : el.getWidth();
38930             h = h !== null ? h : el.getHeight();
38931             this.panelSize = {width: w, height: h};
38932             this.activePanel.setSize(w, h);
38933         }
38934         if(Roo.isIE && this.tabs){
38935             this.tabs.el.repaint();
38936         }
38937     },
38938
38939     /**
38940      * Returns the container element for this region.
38941      * @return {Roo.Element}
38942      */
38943     getEl : function(){
38944         return this.el;
38945     },
38946
38947     /**
38948      * Hides this region.
38949      */
38950     hide : function(){
38951         //if(!this.collapsed){
38952             this.el.dom.style.left = "-2000px";
38953             this.el.hide();
38954         //}else{
38955          //   this.collapsedEl.dom.style.left = "-2000px";
38956          //   this.collapsedEl.hide();
38957        // }
38958         this.visible = false;
38959         this.fireEvent("visibilitychange", this, false);
38960     },
38961
38962     /**
38963      * Shows this region if it was previously hidden.
38964      */
38965     show : function(){
38966         //if(!this.collapsed){
38967             this.el.show();
38968         //}else{
38969         //    this.collapsedEl.show();
38970        // }
38971         this.visible = true;
38972         this.fireEvent("visibilitychange", this, true);
38973     },
38974 /*
38975     closeClicked : function(){
38976         if(this.activePanel){
38977             this.remove(this.activePanel);
38978         }
38979     },
38980
38981     collapseClick : function(e){
38982         if(this.isSlid){
38983            e.stopPropagation();
38984            this.slideIn();
38985         }else{
38986            e.stopPropagation();
38987            this.slideOut();
38988         }
38989     },
38990 */
38991     /**
38992      * Collapses this region.
38993      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38994      */
38995     /*
38996     collapse : function(skipAnim, skipCheck = false){
38997         if(this.collapsed) {
38998             return;
38999         }
39000         
39001         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39002             
39003             this.collapsed = true;
39004             if(this.split){
39005                 this.split.el.hide();
39006             }
39007             if(this.config.animate && skipAnim !== true){
39008                 this.fireEvent("invalidated", this);
39009                 this.animateCollapse();
39010             }else{
39011                 this.el.setLocation(-20000,-20000);
39012                 this.el.hide();
39013                 this.collapsedEl.show();
39014                 this.fireEvent("collapsed", this);
39015                 this.fireEvent("invalidated", this);
39016             }
39017         }
39018         
39019     },
39020 */
39021     animateCollapse : function(){
39022         // overridden
39023     },
39024
39025     /**
39026      * Expands this region if it was previously collapsed.
39027      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39028      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39029      */
39030     /*
39031     expand : function(e, skipAnim){
39032         if(e) {
39033             e.stopPropagation();
39034         }
39035         if(!this.collapsed || this.el.hasActiveFx()) {
39036             return;
39037         }
39038         if(this.isSlid){
39039             this.afterSlideIn();
39040             skipAnim = true;
39041         }
39042         this.collapsed = false;
39043         if(this.config.animate && skipAnim !== true){
39044             this.animateExpand();
39045         }else{
39046             this.el.show();
39047             if(this.split){
39048                 this.split.el.show();
39049             }
39050             this.collapsedEl.setLocation(-2000,-2000);
39051             this.collapsedEl.hide();
39052             this.fireEvent("invalidated", this);
39053             this.fireEvent("expanded", this);
39054         }
39055     },
39056 */
39057     animateExpand : function(){
39058         // overridden
39059     },
39060
39061     initTabs : function()
39062     {
39063         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39064         
39065         var ts = new Roo.bootstrap.panel.Tabs({
39066             el: this.bodyEl.dom,
39067             region : this,
39068             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39069             disableTooltips: this.config.disableTabTips,
39070             toolbar : this.config.toolbar
39071         });
39072         
39073         if(this.config.hideTabs){
39074             ts.stripWrap.setDisplayed(false);
39075         }
39076         this.tabs = ts;
39077         ts.resizeTabs = this.config.resizeTabs === true;
39078         ts.minTabWidth = this.config.minTabWidth || 40;
39079         ts.maxTabWidth = this.config.maxTabWidth || 250;
39080         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39081         ts.monitorResize = false;
39082         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39083         ts.bodyEl.addClass('roo-layout-tabs-body');
39084         this.panels.each(this.initPanelAsTab, this);
39085     },
39086
39087     initPanelAsTab : function(panel){
39088         var ti = this.tabs.addTab(
39089             panel.getEl().id,
39090             panel.getTitle(),
39091             null,
39092             this.config.closeOnTab && panel.isClosable(),
39093             panel.tpl
39094         );
39095         if(panel.tabTip !== undefined){
39096             ti.setTooltip(panel.tabTip);
39097         }
39098         ti.on("activate", function(){
39099               this.setActivePanel(panel);
39100         }, this);
39101         
39102         if(this.config.closeOnTab){
39103             ti.on("beforeclose", function(t, e){
39104                 e.cancel = true;
39105                 this.remove(panel);
39106             }, this);
39107         }
39108         
39109         panel.tabItem = ti;
39110         
39111         return ti;
39112     },
39113
39114     updatePanelTitle : function(panel, title)
39115     {
39116         if(this.activePanel == panel){
39117             this.updateTitle(title);
39118         }
39119         if(this.tabs){
39120             var ti = this.tabs.getTab(panel.getEl().id);
39121             ti.setText(title);
39122             if(panel.tabTip !== undefined){
39123                 ti.setTooltip(panel.tabTip);
39124             }
39125         }
39126     },
39127
39128     updateTitle : function(title){
39129         if(this.titleTextEl && !this.config.title){
39130             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39131         }
39132     },
39133
39134     setActivePanel : function(panel)
39135     {
39136         panel = this.getPanel(panel);
39137         if(this.activePanel && this.activePanel != panel){
39138             if(this.activePanel.setActiveState(false) === false){
39139                 return;
39140             }
39141         }
39142         this.activePanel = panel;
39143         panel.setActiveState(true);
39144         if(this.panelSize){
39145             panel.setSize(this.panelSize.width, this.panelSize.height);
39146         }
39147         if(this.closeBtn){
39148             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39149         }
39150         this.updateTitle(panel.getTitle());
39151         if(this.tabs){
39152             this.fireEvent("invalidated", this);
39153         }
39154         this.fireEvent("panelactivated", this, panel);
39155     },
39156
39157     /**
39158      * Shows the specified panel.
39159      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39160      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39161      */
39162     showPanel : function(panel)
39163     {
39164         panel = this.getPanel(panel);
39165         if(panel){
39166             if(this.tabs){
39167                 var tab = this.tabs.getTab(panel.getEl().id);
39168                 if(tab.isHidden()){
39169                     this.tabs.unhideTab(tab.id);
39170                 }
39171                 tab.activate();
39172             }else{
39173                 this.setActivePanel(panel);
39174             }
39175         }
39176         return panel;
39177     },
39178
39179     /**
39180      * Get the active panel for this region.
39181      * @return {Roo.ContentPanel} The active panel or null
39182      */
39183     getActivePanel : function(){
39184         return this.activePanel;
39185     },
39186
39187     validateVisibility : function(){
39188         if(this.panels.getCount() < 1){
39189             this.updateTitle("&#160;");
39190             this.closeBtn.hide();
39191             this.hide();
39192         }else{
39193             if(!this.isVisible()){
39194                 this.show();
39195             }
39196         }
39197     },
39198
39199     /**
39200      * Adds the passed ContentPanel(s) to this region.
39201      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39202      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39203      */
39204     add : function(panel)
39205     {
39206         if(arguments.length > 1){
39207             for(var i = 0, len = arguments.length; i < len; i++) {
39208                 this.add(arguments[i]);
39209             }
39210             return null;
39211         }
39212         
39213         // if we have not been rendered yet, then we can not really do much of this..
39214         if (!this.bodyEl) {
39215             this.unrendered_panels.push(panel);
39216             return panel;
39217         }
39218         
39219         
39220         
39221         
39222         if(this.hasPanel(panel)){
39223             this.showPanel(panel);
39224             return panel;
39225         }
39226         panel.setRegion(this);
39227         this.panels.add(panel);
39228        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39229             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39230             // and hide them... ???
39231             this.bodyEl.dom.appendChild(panel.getEl().dom);
39232             if(panel.background !== true){
39233                 this.setActivePanel(panel);
39234             }
39235             this.fireEvent("paneladded", this, panel);
39236             return panel;
39237         }
39238         */
39239         if(!this.tabs){
39240             this.initTabs();
39241         }else{
39242             this.initPanelAsTab(panel);
39243         }
39244         
39245         
39246         if(panel.background !== true){
39247             this.tabs.activate(panel.getEl().id);
39248         }
39249         this.fireEvent("paneladded", this, panel);
39250         return panel;
39251     },
39252
39253     /**
39254      * Hides the tab for the specified panel.
39255      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39256      */
39257     hidePanel : function(panel){
39258         if(this.tabs && (panel = this.getPanel(panel))){
39259             this.tabs.hideTab(panel.getEl().id);
39260         }
39261     },
39262
39263     /**
39264      * Unhides the tab for a previously hidden panel.
39265      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39266      */
39267     unhidePanel : function(panel){
39268         if(this.tabs && (panel = this.getPanel(panel))){
39269             this.tabs.unhideTab(panel.getEl().id);
39270         }
39271     },
39272
39273     clearPanels : function(){
39274         while(this.panels.getCount() > 0){
39275              this.remove(this.panels.first());
39276         }
39277     },
39278
39279     /**
39280      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39281      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39282      * @param {Boolean} preservePanel Overrides the config preservePanel option
39283      * @return {Roo.ContentPanel} The panel that was removed
39284      */
39285     remove : function(panel, preservePanel)
39286     {
39287         panel = this.getPanel(panel);
39288         if(!panel){
39289             return null;
39290         }
39291         var e = {};
39292         this.fireEvent("beforeremove", this, panel, e);
39293         if(e.cancel === true){
39294             return null;
39295         }
39296         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39297         var panelId = panel.getId();
39298         this.panels.removeKey(panelId);
39299         if(preservePanel){
39300             document.body.appendChild(panel.getEl().dom);
39301         }
39302         if(this.tabs){
39303             this.tabs.removeTab(panel.getEl().id);
39304         }else if (!preservePanel){
39305             this.bodyEl.dom.removeChild(panel.getEl().dom);
39306         }
39307         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39308             var p = this.panels.first();
39309             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39310             tempEl.appendChild(p.getEl().dom);
39311             this.bodyEl.update("");
39312             this.bodyEl.dom.appendChild(p.getEl().dom);
39313             tempEl = null;
39314             this.updateTitle(p.getTitle());
39315             this.tabs = null;
39316             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39317             this.setActivePanel(p);
39318         }
39319         panel.setRegion(null);
39320         if(this.activePanel == panel){
39321             this.activePanel = null;
39322         }
39323         if(this.config.autoDestroy !== false && preservePanel !== true){
39324             try{panel.destroy();}catch(e){}
39325         }
39326         this.fireEvent("panelremoved", this, panel);
39327         return panel;
39328     },
39329
39330     /**
39331      * Returns the TabPanel component used by this region
39332      * @return {Roo.TabPanel}
39333      */
39334     getTabs : function(){
39335         return this.tabs;
39336     },
39337
39338     createTool : function(parentEl, className){
39339         var btn = Roo.DomHelper.append(parentEl, {
39340             tag: "div",
39341             cls: "x-layout-tools-button",
39342             children: [ {
39343                 tag: "div",
39344                 cls: "roo-layout-tools-button-inner " + className,
39345                 html: "&#160;"
39346             }]
39347         }, true);
39348         btn.addClassOnOver("roo-layout-tools-button-over");
39349         return btn;
39350     }
39351 });/*
39352  * Based on:
39353  * Ext JS Library 1.1.1
39354  * Copyright(c) 2006-2007, Ext JS, LLC.
39355  *
39356  * Originally Released Under LGPL - original licence link has changed is not relivant.
39357  *
39358  * Fork - LGPL
39359  * <script type="text/javascript">
39360  */
39361  
39362
39363
39364 /**
39365  * @class Roo.SplitLayoutRegion
39366  * @extends Roo.LayoutRegion
39367  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39368  */
39369 Roo.bootstrap.layout.Split = function(config){
39370     this.cursor = config.cursor;
39371     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39372 };
39373
39374 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39375 {
39376     splitTip : "Drag to resize.",
39377     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39378     useSplitTips : false,
39379
39380     applyConfig : function(config){
39381         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39382     },
39383     
39384     onRender : function(ctr,pos) {
39385         
39386         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39387         if(!this.config.split){
39388             return;
39389         }
39390         if(!this.split){
39391             
39392             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39393                             tag: "div",
39394                             id: this.el.id + "-split",
39395                             cls: "roo-layout-split roo-layout-split-"+this.position,
39396                             html: "&#160;"
39397             });
39398             /** The SplitBar for this region 
39399             * @type Roo.SplitBar */
39400             // does not exist yet...
39401             Roo.log([this.position, this.orientation]);
39402             
39403             this.split = new Roo.bootstrap.SplitBar({
39404                 dragElement : splitEl,
39405                 resizingElement: this.el,
39406                 orientation : this.orientation
39407             });
39408             
39409             this.split.on("moved", this.onSplitMove, this);
39410             this.split.useShim = this.config.useShim === true;
39411             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39412             if(this.useSplitTips){
39413                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39414             }
39415             //if(config.collapsible){
39416             //    this.split.el.on("dblclick", this.collapse,  this);
39417             //}
39418         }
39419         if(typeof this.config.minSize != "undefined"){
39420             this.split.minSize = this.config.minSize;
39421         }
39422         if(typeof this.config.maxSize != "undefined"){
39423             this.split.maxSize = this.config.maxSize;
39424         }
39425         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39426             this.hideSplitter();
39427         }
39428         
39429     },
39430
39431     getHMaxSize : function(){
39432          var cmax = this.config.maxSize || 10000;
39433          var center = this.mgr.getRegion("center");
39434          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39435     },
39436
39437     getVMaxSize : function(){
39438          var cmax = this.config.maxSize || 10000;
39439          var center = this.mgr.getRegion("center");
39440          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39441     },
39442
39443     onSplitMove : function(split, newSize){
39444         this.fireEvent("resized", this, newSize);
39445     },
39446     
39447     /** 
39448      * Returns the {@link Roo.SplitBar} for this region.
39449      * @return {Roo.SplitBar}
39450      */
39451     getSplitBar : function(){
39452         return this.split;
39453     },
39454     
39455     hide : function(){
39456         this.hideSplitter();
39457         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39458     },
39459
39460     hideSplitter : function(){
39461         if(this.split){
39462             this.split.el.setLocation(-2000,-2000);
39463             this.split.el.hide();
39464         }
39465     },
39466
39467     show : function(){
39468         if(this.split){
39469             this.split.el.show();
39470         }
39471         Roo.bootstrap.layout.Split.superclass.show.call(this);
39472     },
39473     
39474     beforeSlide: function(){
39475         if(Roo.isGecko){// firefox overflow auto bug workaround
39476             this.bodyEl.clip();
39477             if(this.tabs) {
39478                 this.tabs.bodyEl.clip();
39479             }
39480             if(this.activePanel){
39481                 this.activePanel.getEl().clip();
39482                 
39483                 if(this.activePanel.beforeSlide){
39484                     this.activePanel.beforeSlide();
39485                 }
39486             }
39487         }
39488     },
39489     
39490     afterSlide : function(){
39491         if(Roo.isGecko){// firefox overflow auto bug workaround
39492             this.bodyEl.unclip();
39493             if(this.tabs) {
39494                 this.tabs.bodyEl.unclip();
39495             }
39496             if(this.activePanel){
39497                 this.activePanel.getEl().unclip();
39498                 if(this.activePanel.afterSlide){
39499                     this.activePanel.afterSlide();
39500                 }
39501             }
39502         }
39503     },
39504
39505     initAutoHide : function(){
39506         if(this.autoHide !== false){
39507             if(!this.autoHideHd){
39508                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39509                 this.autoHideHd = {
39510                     "mouseout": function(e){
39511                         if(!e.within(this.el, true)){
39512                             st.delay(500);
39513                         }
39514                     },
39515                     "mouseover" : function(e){
39516                         st.cancel();
39517                     },
39518                     scope : this
39519                 };
39520             }
39521             this.el.on(this.autoHideHd);
39522         }
39523     },
39524
39525     clearAutoHide : function(){
39526         if(this.autoHide !== false){
39527             this.el.un("mouseout", this.autoHideHd.mouseout);
39528             this.el.un("mouseover", this.autoHideHd.mouseover);
39529         }
39530     },
39531
39532     clearMonitor : function(){
39533         Roo.get(document).un("click", this.slideInIf, this);
39534     },
39535
39536     // these names are backwards but not changed for compat
39537     slideOut : function(){
39538         if(this.isSlid || this.el.hasActiveFx()){
39539             return;
39540         }
39541         this.isSlid = true;
39542         if(this.collapseBtn){
39543             this.collapseBtn.hide();
39544         }
39545         this.closeBtnState = this.closeBtn.getStyle('display');
39546         this.closeBtn.hide();
39547         if(this.stickBtn){
39548             this.stickBtn.show();
39549         }
39550         this.el.show();
39551         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39552         this.beforeSlide();
39553         this.el.setStyle("z-index", 10001);
39554         this.el.slideIn(this.getSlideAnchor(), {
39555             callback: function(){
39556                 this.afterSlide();
39557                 this.initAutoHide();
39558                 Roo.get(document).on("click", this.slideInIf, this);
39559                 this.fireEvent("slideshow", this);
39560             },
39561             scope: this,
39562             block: true
39563         });
39564     },
39565
39566     afterSlideIn : function(){
39567         this.clearAutoHide();
39568         this.isSlid = false;
39569         this.clearMonitor();
39570         this.el.setStyle("z-index", "");
39571         if(this.collapseBtn){
39572             this.collapseBtn.show();
39573         }
39574         this.closeBtn.setStyle('display', this.closeBtnState);
39575         if(this.stickBtn){
39576             this.stickBtn.hide();
39577         }
39578         this.fireEvent("slidehide", this);
39579     },
39580
39581     slideIn : function(cb){
39582         if(!this.isSlid || this.el.hasActiveFx()){
39583             Roo.callback(cb);
39584             return;
39585         }
39586         this.isSlid = false;
39587         this.beforeSlide();
39588         this.el.slideOut(this.getSlideAnchor(), {
39589             callback: function(){
39590                 this.el.setLeftTop(-10000, -10000);
39591                 this.afterSlide();
39592                 this.afterSlideIn();
39593                 Roo.callback(cb);
39594             },
39595             scope: this,
39596             block: true
39597         });
39598     },
39599     
39600     slideInIf : function(e){
39601         if(!e.within(this.el)){
39602             this.slideIn();
39603         }
39604     },
39605
39606     animateCollapse : function(){
39607         this.beforeSlide();
39608         this.el.setStyle("z-index", 20000);
39609         var anchor = this.getSlideAnchor();
39610         this.el.slideOut(anchor, {
39611             callback : function(){
39612                 this.el.setStyle("z-index", "");
39613                 this.collapsedEl.slideIn(anchor, {duration:.3});
39614                 this.afterSlide();
39615                 this.el.setLocation(-10000,-10000);
39616                 this.el.hide();
39617                 this.fireEvent("collapsed", this);
39618             },
39619             scope: this,
39620             block: true
39621         });
39622     },
39623
39624     animateExpand : function(){
39625         this.beforeSlide();
39626         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39627         this.el.setStyle("z-index", 20000);
39628         this.collapsedEl.hide({
39629             duration:.1
39630         });
39631         this.el.slideIn(this.getSlideAnchor(), {
39632             callback : function(){
39633                 this.el.setStyle("z-index", "");
39634                 this.afterSlide();
39635                 if(this.split){
39636                     this.split.el.show();
39637                 }
39638                 this.fireEvent("invalidated", this);
39639                 this.fireEvent("expanded", this);
39640             },
39641             scope: this,
39642             block: true
39643         });
39644     },
39645
39646     anchors : {
39647         "west" : "left",
39648         "east" : "right",
39649         "north" : "top",
39650         "south" : "bottom"
39651     },
39652
39653     sanchors : {
39654         "west" : "l",
39655         "east" : "r",
39656         "north" : "t",
39657         "south" : "b"
39658     },
39659
39660     canchors : {
39661         "west" : "tl-tr",
39662         "east" : "tr-tl",
39663         "north" : "tl-bl",
39664         "south" : "bl-tl"
39665     },
39666
39667     getAnchor : function(){
39668         return this.anchors[this.position];
39669     },
39670
39671     getCollapseAnchor : function(){
39672         return this.canchors[this.position];
39673     },
39674
39675     getSlideAnchor : function(){
39676         return this.sanchors[this.position];
39677     },
39678
39679     getAlignAdj : function(){
39680         var cm = this.cmargins;
39681         switch(this.position){
39682             case "west":
39683                 return [0, 0];
39684             break;
39685             case "east":
39686                 return [0, 0];
39687             break;
39688             case "north":
39689                 return [0, 0];
39690             break;
39691             case "south":
39692                 return [0, 0];
39693             break;
39694         }
39695     },
39696
39697     getExpandAdj : function(){
39698         var c = this.collapsedEl, cm = this.cmargins;
39699         switch(this.position){
39700             case "west":
39701                 return [-(cm.right+c.getWidth()+cm.left), 0];
39702             break;
39703             case "east":
39704                 return [cm.right+c.getWidth()+cm.left, 0];
39705             break;
39706             case "north":
39707                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39708             break;
39709             case "south":
39710                 return [0, cm.top+cm.bottom+c.getHeight()];
39711             break;
39712         }
39713     }
39714 });/*
39715  * Based on:
39716  * Ext JS Library 1.1.1
39717  * Copyright(c) 2006-2007, Ext JS, LLC.
39718  *
39719  * Originally Released Under LGPL - original licence link has changed is not relivant.
39720  *
39721  * Fork - LGPL
39722  * <script type="text/javascript">
39723  */
39724 /*
39725  * These classes are private internal classes
39726  */
39727 Roo.bootstrap.layout.Center = function(config){
39728     config.region = "center";
39729     Roo.bootstrap.layout.Region.call(this, config);
39730     this.visible = true;
39731     this.minWidth = config.minWidth || 20;
39732     this.minHeight = config.minHeight || 20;
39733 };
39734
39735 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39736     hide : function(){
39737         // center panel can't be hidden
39738     },
39739     
39740     show : function(){
39741         // center panel can't be hidden
39742     },
39743     
39744     getMinWidth: function(){
39745         return this.minWidth;
39746     },
39747     
39748     getMinHeight: function(){
39749         return this.minHeight;
39750     }
39751 });
39752
39753
39754
39755
39756  
39757
39758
39759
39760
39761
39762
39763 Roo.bootstrap.layout.North = function(config)
39764 {
39765     config.region = 'north';
39766     config.cursor = 'n-resize';
39767     
39768     Roo.bootstrap.layout.Split.call(this, config);
39769     
39770     
39771     if(this.split){
39772         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39773         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39774         this.split.el.addClass("roo-layout-split-v");
39775     }
39776     //var size = config.initialSize || config.height;
39777     //if(this.el && typeof size != "undefined"){
39778     //    this.el.setHeight(size);
39779     //}
39780 };
39781 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39782 {
39783     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39784      
39785      
39786     onRender : function(ctr, pos)
39787     {
39788         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39789         var size = this.config.initialSize || this.config.height;
39790         if(this.el && typeof size != "undefined"){
39791             this.el.setHeight(size);
39792         }
39793     
39794     },
39795     
39796     getBox : function(){
39797         if(this.collapsed){
39798             return this.collapsedEl.getBox();
39799         }
39800         var box = this.el.getBox();
39801         if(this.split){
39802             box.height += this.split.el.getHeight();
39803         }
39804         return box;
39805     },
39806     
39807     updateBox : function(box){
39808         if(this.split && !this.collapsed){
39809             box.height -= this.split.el.getHeight();
39810             this.split.el.setLeft(box.x);
39811             this.split.el.setTop(box.y+box.height);
39812             this.split.el.setWidth(box.width);
39813         }
39814         if(this.collapsed){
39815             this.updateBody(box.width, null);
39816         }
39817         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39818     }
39819 });
39820
39821
39822
39823
39824
39825 Roo.bootstrap.layout.South = function(config){
39826     config.region = 'south';
39827     config.cursor = 's-resize';
39828     Roo.bootstrap.layout.Split.call(this, config);
39829     if(this.split){
39830         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39831         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39832         this.split.el.addClass("roo-layout-split-v");
39833     }
39834     
39835 };
39836
39837 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39838     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39839     
39840     onRender : function(ctr, pos)
39841     {
39842         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39843         var size = this.config.initialSize || this.config.height;
39844         if(this.el && typeof size != "undefined"){
39845             this.el.setHeight(size);
39846         }
39847     
39848     },
39849     
39850     getBox : function(){
39851         if(this.collapsed){
39852             return this.collapsedEl.getBox();
39853         }
39854         var box = this.el.getBox();
39855         if(this.split){
39856             var sh = this.split.el.getHeight();
39857             box.height += sh;
39858             box.y -= sh;
39859         }
39860         return box;
39861     },
39862     
39863     updateBox : function(box){
39864         if(this.split && !this.collapsed){
39865             var sh = this.split.el.getHeight();
39866             box.height -= sh;
39867             box.y += sh;
39868             this.split.el.setLeft(box.x);
39869             this.split.el.setTop(box.y-sh);
39870             this.split.el.setWidth(box.width);
39871         }
39872         if(this.collapsed){
39873             this.updateBody(box.width, null);
39874         }
39875         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39876     }
39877 });
39878
39879 Roo.bootstrap.layout.East = function(config){
39880     config.region = "east";
39881     config.cursor = "e-resize";
39882     Roo.bootstrap.layout.Split.call(this, config);
39883     if(this.split){
39884         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39885         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39886         this.split.el.addClass("roo-layout-split-h");
39887     }
39888     
39889 };
39890 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39891     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39892     
39893     onRender : function(ctr, pos)
39894     {
39895         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39896         var size = this.config.initialSize || this.config.width;
39897         if(this.el && typeof size != "undefined"){
39898             this.el.setWidth(size);
39899         }
39900     
39901     },
39902     
39903     getBox : function(){
39904         if(this.collapsed){
39905             return this.collapsedEl.getBox();
39906         }
39907         var box = this.el.getBox();
39908         if(this.split){
39909             var sw = this.split.el.getWidth();
39910             box.width += sw;
39911             box.x -= sw;
39912         }
39913         return box;
39914     },
39915
39916     updateBox : function(box){
39917         if(this.split && !this.collapsed){
39918             var sw = this.split.el.getWidth();
39919             box.width -= sw;
39920             this.split.el.setLeft(box.x);
39921             this.split.el.setTop(box.y);
39922             this.split.el.setHeight(box.height);
39923             box.x += sw;
39924         }
39925         if(this.collapsed){
39926             this.updateBody(null, box.height);
39927         }
39928         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39929     }
39930 });
39931
39932 Roo.bootstrap.layout.West = function(config){
39933     config.region = "west";
39934     config.cursor = "w-resize";
39935     
39936     Roo.bootstrap.layout.Split.call(this, config);
39937     if(this.split){
39938         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39939         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39940         this.split.el.addClass("roo-layout-split-h");
39941     }
39942     
39943 };
39944 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39945     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39946     
39947     onRender: function(ctr, pos)
39948     {
39949         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39950         var size = this.config.initialSize || this.config.width;
39951         if(typeof size != "undefined"){
39952             this.el.setWidth(size);
39953         }
39954     },
39955     
39956     getBox : function(){
39957         if(this.collapsed){
39958             return this.collapsedEl.getBox();
39959         }
39960         var box = this.el.getBox();
39961         if (box.width == 0) {
39962             box.width = this.config.width; // kludge?
39963         }
39964         if(this.split){
39965             box.width += this.split.el.getWidth();
39966         }
39967         return box;
39968     },
39969     
39970     updateBox : function(box){
39971         if(this.split && !this.collapsed){
39972             var sw = this.split.el.getWidth();
39973             box.width -= sw;
39974             this.split.el.setLeft(box.x+box.width);
39975             this.split.el.setTop(box.y);
39976             this.split.el.setHeight(box.height);
39977         }
39978         if(this.collapsed){
39979             this.updateBody(null, box.height);
39980         }
39981         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39982     }
39983 });Roo.namespace("Roo.bootstrap.panel");/*
39984  * Based on:
39985  * Ext JS Library 1.1.1
39986  * Copyright(c) 2006-2007, Ext JS, LLC.
39987  *
39988  * Originally Released Under LGPL - original licence link has changed is not relivant.
39989  *
39990  * Fork - LGPL
39991  * <script type="text/javascript">
39992  */
39993 /**
39994  * @class Roo.ContentPanel
39995  * @extends Roo.util.Observable
39996  * A basic ContentPanel element.
39997  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39998  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39999  * @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
40000  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40001  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40002  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40003  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40004  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40005  * @cfg {String} title          The title for this panel
40006  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40007  * @cfg {String} url            Calls {@link #setUrl} with this value
40008  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40009  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40010  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40011  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40012  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40013  * @cfg {Boolean} badges render the badges
40014  * @cfg {String} cls  extra classes to use  
40015  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40016
40017  * @constructor
40018  * Create a new ContentPanel.
40019  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40020  * @param {String/Object} config A string to set only the title or a config object
40021  * @param {String} content (optional) Set the HTML content for this panel
40022  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40023  */
40024 Roo.bootstrap.panel.Content = function( config){
40025     
40026     this.tpl = config.tpl || false;
40027     
40028     var el = config.el;
40029     var content = config.content;
40030
40031     if(config.autoCreate){ // xtype is available if this is called from factory
40032         el = Roo.id();
40033     }
40034     this.el = Roo.get(el);
40035     if(!this.el && config && config.autoCreate){
40036         if(typeof config.autoCreate == "object"){
40037             if(!config.autoCreate.id){
40038                 config.autoCreate.id = config.id||el;
40039             }
40040             this.el = Roo.DomHelper.append(document.body,
40041                         config.autoCreate, true);
40042         }else{
40043             var elcfg =  {
40044                 tag: "div",
40045                 cls: (config.cls || '') +
40046                     (config.background ? ' bg-' + config.background : '') +
40047                     " roo-layout-inactive-content",
40048                 id: config.id||el
40049             };
40050             if (config.iframe) {
40051                 elcfg.cn = [
40052                     {
40053                         tag : 'iframe',
40054                         style : 'border: 0px',
40055                         src : 'about:blank'
40056                     }
40057                 ];
40058             }
40059               
40060             if (config.html) {
40061                 elcfg.html = config.html;
40062                 
40063             }
40064                         
40065             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40066             if (config.iframe) {
40067                 this.iframeEl = this.el.select('iframe',true).first();
40068             }
40069             
40070         }
40071     } 
40072     this.closable = false;
40073     this.loaded = false;
40074     this.active = false;
40075    
40076       
40077     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40078         
40079         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40080         
40081         this.wrapEl = this.el; //this.el.wrap();
40082         var ti = [];
40083         if (config.toolbar.items) {
40084             ti = config.toolbar.items ;
40085             delete config.toolbar.items ;
40086         }
40087         
40088         var nitems = [];
40089         this.toolbar.render(this.wrapEl, 'before');
40090         for(var i =0;i < ti.length;i++) {
40091           //  Roo.log(['add child', items[i]]);
40092             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40093         }
40094         this.toolbar.items = nitems;
40095         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40096         delete config.toolbar;
40097         
40098     }
40099     /*
40100     // xtype created footer. - not sure if will work as we normally have to render first..
40101     if (this.footer && !this.footer.el && this.footer.xtype) {
40102         if (!this.wrapEl) {
40103             this.wrapEl = this.el.wrap();
40104         }
40105     
40106         this.footer.container = this.wrapEl.createChild();
40107          
40108         this.footer = Roo.factory(this.footer, Roo);
40109         
40110     }
40111     */
40112     
40113      if(typeof config == "string"){
40114         this.title = config;
40115     }else{
40116         Roo.apply(this, config);
40117     }
40118     
40119     if(this.resizeEl){
40120         this.resizeEl = Roo.get(this.resizeEl, true);
40121     }else{
40122         this.resizeEl = this.el;
40123     }
40124     // handle view.xtype
40125     
40126  
40127     
40128     
40129     this.addEvents({
40130         /**
40131          * @event activate
40132          * Fires when this panel is activated. 
40133          * @param {Roo.ContentPanel} this
40134          */
40135         "activate" : true,
40136         /**
40137          * @event deactivate
40138          * Fires when this panel is activated. 
40139          * @param {Roo.ContentPanel} this
40140          */
40141         "deactivate" : true,
40142
40143         /**
40144          * @event resize
40145          * Fires when this panel is resized if fitToFrame is true.
40146          * @param {Roo.ContentPanel} this
40147          * @param {Number} width The width after any component adjustments
40148          * @param {Number} height The height after any component adjustments
40149          */
40150         "resize" : true,
40151         
40152          /**
40153          * @event render
40154          * Fires when this tab is created
40155          * @param {Roo.ContentPanel} this
40156          */
40157         "render" : true
40158         
40159         
40160         
40161     });
40162     
40163
40164     
40165     
40166     if(this.autoScroll && !this.iframe){
40167         this.resizeEl.setStyle("overflow", "auto");
40168     } else {
40169         // fix randome scrolling
40170         //this.el.on('scroll', function() {
40171         //    Roo.log('fix random scolling');
40172         //    this.scrollTo('top',0); 
40173         //});
40174     }
40175     content = content || this.content;
40176     if(content){
40177         this.setContent(content);
40178     }
40179     if(config && config.url){
40180         this.setUrl(this.url, this.params, this.loadOnce);
40181     }
40182     
40183     
40184     
40185     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40186     
40187     if (this.view && typeof(this.view.xtype) != 'undefined') {
40188         this.view.el = this.el.appendChild(document.createElement("div"));
40189         this.view = Roo.factory(this.view); 
40190         this.view.render  &&  this.view.render(false, '');  
40191     }
40192     
40193     
40194     this.fireEvent('render', this);
40195 };
40196
40197 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40198     
40199     cls : '',
40200     background : '',
40201     
40202     tabTip : '',
40203     
40204     iframe : false,
40205     iframeEl : false,
40206     
40207     setRegion : function(region){
40208         this.region = region;
40209         this.setActiveClass(region && !this.background);
40210     },
40211     
40212     
40213     setActiveClass: function(state)
40214     {
40215         if(state){
40216            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40217            this.el.setStyle('position','relative');
40218         }else{
40219            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40220            this.el.setStyle('position', 'absolute');
40221         } 
40222     },
40223     
40224     /**
40225      * Returns the toolbar for this Panel if one was configured. 
40226      * @return {Roo.Toolbar} 
40227      */
40228     getToolbar : function(){
40229         return this.toolbar;
40230     },
40231     
40232     setActiveState : function(active)
40233     {
40234         this.active = active;
40235         this.setActiveClass(active);
40236         if(!active){
40237             if(this.fireEvent("deactivate", this) === false){
40238                 return false;
40239             }
40240             return true;
40241         }
40242         this.fireEvent("activate", this);
40243         return true;
40244     },
40245     /**
40246      * Updates this panel's element (not for iframe)
40247      * @param {String} content The new content
40248      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40249     */
40250     setContent : function(content, loadScripts){
40251         if (this.iframe) {
40252             return;
40253         }
40254         
40255         this.el.update(content, loadScripts);
40256     },
40257
40258     ignoreResize : function(w, h){
40259         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40260             return true;
40261         }else{
40262             this.lastSize = {width: w, height: h};
40263             return false;
40264         }
40265     },
40266     /**
40267      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40268      * @return {Roo.UpdateManager} The UpdateManager
40269      */
40270     getUpdateManager : function(){
40271         if (this.iframe) {
40272             return false;
40273         }
40274         return this.el.getUpdateManager();
40275     },
40276      /**
40277      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40278      * Does not work with IFRAME contents
40279      * @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:
40280 <pre><code>
40281 panel.load({
40282     url: "your-url.php",
40283     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40284     callback: yourFunction,
40285     scope: yourObject, //(optional scope)
40286     discardUrl: false,
40287     nocache: false,
40288     text: "Loading...",
40289     timeout: 30,
40290     scripts: false
40291 });
40292 </code></pre>
40293      
40294      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40295      * 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.
40296      * @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}
40297      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40298      * @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.
40299      * @return {Roo.ContentPanel} this
40300      */
40301     load : function(){
40302         
40303         if (this.iframe) {
40304             return this;
40305         }
40306         
40307         var um = this.el.getUpdateManager();
40308         um.update.apply(um, arguments);
40309         return this;
40310     },
40311
40312
40313     /**
40314      * 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.
40315      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40316      * @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)
40317      * @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)
40318      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40319      */
40320     setUrl : function(url, params, loadOnce){
40321         if (this.iframe) {
40322             this.iframeEl.dom.src = url;
40323             return false;
40324         }
40325         
40326         if(this.refreshDelegate){
40327             this.removeListener("activate", this.refreshDelegate);
40328         }
40329         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40330         this.on("activate", this.refreshDelegate);
40331         return this.el.getUpdateManager();
40332     },
40333     
40334     _handleRefresh : function(url, params, loadOnce){
40335         if(!loadOnce || !this.loaded){
40336             var updater = this.el.getUpdateManager();
40337             updater.update(url, params, this._setLoaded.createDelegate(this));
40338         }
40339     },
40340     
40341     _setLoaded : function(){
40342         this.loaded = true;
40343     }, 
40344     
40345     /**
40346      * Returns this panel's id
40347      * @return {String} 
40348      */
40349     getId : function(){
40350         return this.el.id;
40351     },
40352     
40353     /** 
40354      * Returns this panel's element - used by regiosn to add.
40355      * @return {Roo.Element} 
40356      */
40357     getEl : function(){
40358         return this.wrapEl || this.el;
40359     },
40360     
40361    
40362     
40363     adjustForComponents : function(width, height)
40364     {
40365         //Roo.log('adjustForComponents ');
40366         if(this.resizeEl != this.el){
40367             width -= this.el.getFrameWidth('lr');
40368             height -= this.el.getFrameWidth('tb');
40369         }
40370         if(this.toolbar){
40371             var te = this.toolbar.getEl();
40372             te.setWidth(width);
40373             height -= te.getHeight();
40374         }
40375         if(this.footer){
40376             var te = this.footer.getEl();
40377             te.setWidth(width);
40378             height -= te.getHeight();
40379         }
40380         
40381         
40382         if(this.adjustments){
40383             width += this.adjustments[0];
40384             height += this.adjustments[1];
40385         }
40386         return {"width": width, "height": height};
40387     },
40388     
40389     setSize : function(width, height){
40390         if(this.fitToFrame && !this.ignoreResize(width, height)){
40391             if(this.fitContainer && this.resizeEl != this.el){
40392                 this.el.setSize(width, height);
40393             }
40394             var size = this.adjustForComponents(width, height);
40395             if (this.iframe) {
40396                 this.iframeEl.setSize(width,height);
40397             }
40398             
40399             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40400             this.fireEvent('resize', this, size.width, size.height);
40401             
40402             
40403         }
40404     },
40405     
40406     /**
40407      * Returns this panel's title
40408      * @return {String} 
40409      */
40410     getTitle : function(){
40411         
40412         if (typeof(this.title) != 'object') {
40413             return this.title;
40414         }
40415         
40416         var t = '';
40417         for (var k in this.title) {
40418             if (!this.title.hasOwnProperty(k)) {
40419                 continue;
40420             }
40421             
40422             if (k.indexOf('-') >= 0) {
40423                 var s = k.split('-');
40424                 for (var i = 0; i<s.length; i++) {
40425                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40426                 }
40427             } else {
40428                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40429             }
40430         }
40431         return t;
40432     },
40433     
40434     /**
40435      * Set this panel's title
40436      * @param {String} title
40437      */
40438     setTitle : function(title){
40439         this.title = title;
40440         if(this.region){
40441             this.region.updatePanelTitle(this, title);
40442         }
40443     },
40444     
40445     /**
40446      * Returns true is this panel was configured to be closable
40447      * @return {Boolean} 
40448      */
40449     isClosable : function(){
40450         return this.closable;
40451     },
40452     
40453     beforeSlide : function(){
40454         this.el.clip();
40455         this.resizeEl.clip();
40456     },
40457     
40458     afterSlide : function(){
40459         this.el.unclip();
40460         this.resizeEl.unclip();
40461     },
40462     
40463     /**
40464      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40465      *   Will fail silently if the {@link #setUrl} method has not been called.
40466      *   This does not activate the panel, just updates its content.
40467      */
40468     refresh : function(){
40469         if(this.refreshDelegate){
40470            this.loaded = false;
40471            this.refreshDelegate();
40472         }
40473     },
40474     
40475     /**
40476      * Destroys this panel
40477      */
40478     destroy : function(){
40479         this.el.removeAllListeners();
40480         var tempEl = document.createElement("span");
40481         tempEl.appendChild(this.el.dom);
40482         tempEl.innerHTML = "";
40483         this.el.remove();
40484         this.el = null;
40485     },
40486     
40487     /**
40488      * form - if the content panel contains a form - this is a reference to it.
40489      * @type {Roo.form.Form}
40490      */
40491     form : false,
40492     /**
40493      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40494      *    This contains a reference to it.
40495      * @type {Roo.View}
40496      */
40497     view : false,
40498     
40499       /**
40500      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40501      * <pre><code>
40502
40503 layout.addxtype({
40504        xtype : 'Form',
40505        items: [ .... ]
40506    }
40507 );
40508
40509 </code></pre>
40510      * @param {Object} cfg Xtype definition of item to add.
40511      */
40512     
40513     
40514     getChildContainer: function () {
40515         return this.getEl();
40516     }
40517     
40518     
40519     /*
40520         var  ret = new Roo.factory(cfg);
40521         return ret;
40522         
40523         
40524         // add form..
40525         if (cfg.xtype.match(/^Form$/)) {
40526             
40527             var el;
40528             //if (this.footer) {
40529             //    el = this.footer.container.insertSibling(false, 'before');
40530             //} else {
40531                 el = this.el.createChild();
40532             //}
40533
40534             this.form = new  Roo.form.Form(cfg);
40535             
40536             
40537             if ( this.form.allItems.length) {
40538                 this.form.render(el.dom);
40539             }
40540             return this.form;
40541         }
40542         // should only have one of theses..
40543         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40544             // views.. should not be just added - used named prop 'view''
40545             
40546             cfg.el = this.el.appendChild(document.createElement("div"));
40547             // factory?
40548             
40549             var ret = new Roo.factory(cfg);
40550              
40551              ret.render && ret.render(false, ''); // render blank..
40552             this.view = ret;
40553             return ret;
40554         }
40555         return false;
40556     }
40557     \*/
40558 });
40559  
40560 /**
40561  * @class Roo.bootstrap.panel.Grid
40562  * @extends Roo.bootstrap.panel.Content
40563  * @constructor
40564  * Create a new GridPanel.
40565  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40566  * @param {Object} config A the config object
40567   
40568  */
40569
40570
40571
40572 Roo.bootstrap.panel.Grid = function(config)
40573 {
40574     
40575       
40576     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40577         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40578
40579     config.el = this.wrapper;
40580     //this.el = this.wrapper;
40581     
40582       if (config.container) {
40583         // ctor'ed from a Border/panel.grid
40584         
40585         
40586         this.wrapper.setStyle("overflow", "hidden");
40587         this.wrapper.addClass('roo-grid-container');
40588
40589     }
40590     
40591     
40592     if(config.toolbar){
40593         var tool_el = this.wrapper.createChild();    
40594         this.toolbar = Roo.factory(config.toolbar);
40595         var ti = [];
40596         if (config.toolbar.items) {
40597             ti = config.toolbar.items ;
40598             delete config.toolbar.items ;
40599         }
40600         
40601         var nitems = [];
40602         this.toolbar.render(tool_el);
40603         for(var i =0;i < ti.length;i++) {
40604           //  Roo.log(['add child', items[i]]);
40605             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40606         }
40607         this.toolbar.items = nitems;
40608         
40609         delete config.toolbar;
40610     }
40611     
40612     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40613     config.grid.scrollBody = true;;
40614     config.grid.monitorWindowResize = false; // turn off autosizing
40615     config.grid.autoHeight = false;
40616     config.grid.autoWidth = false;
40617     
40618     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40619     
40620     if (config.background) {
40621         // render grid on panel activation (if panel background)
40622         this.on('activate', function(gp) {
40623             if (!gp.grid.rendered) {
40624                 gp.grid.render(this.wrapper);
40625                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40626             }
40627         });
40628             
40629     } else {
40630         this.grid.render(this.wrapper);
40631         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40632
40633     }
40634     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40635     // ??? needed ??? config.el = this.wrapper;
40636     
40637     
40638     
40639   
40640     // xtype created footer. - not sure if will work as we normally have to render first..
40641     if (this.footer && !this.footer.el && this.footer.xtype) {
40642         
40643         var ctr = this.grid.getView().getFooterPanel(true);
40644         this.footer.dataSource = this.grid.dataSource;
40645         this.footer = Roo.factory(this.footer, Roo);
40646         this.footer.render(ctr);
40647         
40648     }
40649     
40650     
40651     
40652     
40653      
40654 };
40655
40656 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40657     getId : function(){
40658         return this.grid.id;
40659     },
40660     
40661     /**
40662      * Returns the grid for this panel
40663      * @return {Roo.bootstrap.Table} 
40664      */
40665     getGrid : function(){
40666         return this.grid;    
40667     },
40668     
40669     setSize : function(width, height){
40670         if(!this.ignoreResize(width, height)){
40671             var grid = this.grid;
40672             var size = this.adjustForComponents(width, height);
40673             // tfoot is not a footer?
40674           
40675             
40676             var gridel = grid.getGridEl();
40677             gridel.setSize(size.width, size.height);
40678             
40679             var tbd = grid.getGridEl().select('tbody', true).first();
40680             var thd = grid.getGridEl().select('thead',true).first();
40681             var tbf= grid.getGridEl().select('tfoot', true).first();
40682
40683             if (tbf) {
40684                 size.height -= tbf.getHeight();
40685             }
40686             if (thd) {
40687                 size.height -= thd.getHeight();
40688             }
40689             
40690             tbd.setSize(size.width, size.height );
40691             // this is for the account management tab -seems to work there.
40692             var thd = grid.getGridEl().select('thead',true).first();
40693             //if (tbd) {
40694             //    tbd.setSize(size.width, size.height - thd.getHeight());
40695             //}
40696              
40697             grid.autoSize();
40698         }
40699     },
40700      
40701     
40702     
40703     beforeSlide : function(){
40704         this.grid.getView().scroller.clip();
40705     },
40706     
40707     afterSlide : function(){
40708         this.grid.getView().scroller.unclip();
40709     },
40710     
40711     destroy : function(){
40712         this.grid.destroy();
40713         delete this.grid;
40714         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40715     }
40716 });
40717
40718 /**
40719  * @class Roo.bootstrap.panel.Nest
40720  * @extends Roo.bootstrap.panel.Content
40721  * @constructor
40722  * Create a new Panel, that can contain a layout.Border.
40723  * 
40724  * 
40725  * @param {Roo.BorderLayout} layout The layout for this panel
40726  * @param {String/Object} config A string to set only the title or a config object
40727  */
40728 Roo.bootstrap.panel.Nest = function(config)
40729 {
40730     // construct with only one argument..
40731     /* FIXME - implement nicer consturctors
40732     if (layout.layout) {
40733         config = layout;
40734         layout = config.layout;
40735         delete config.layout;
40736     }
40737     if (layout.xtype && !layout.getEl) {
40738         // then layout needs constructing..
40739         layout = Roo.factory(layout, Roo);
40740     }
40741     */
40742     
40743     config.el =  config.layout.getEl();
40744     
40745     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40746     
40747     config.layout.monitorWindowResize = false; // turn off autosizing
40748     this.layout = config.layout;
40749     this.layout.getEl().addClass("roo-layout-nested-layout");
40750     this.layout.parent = this;
40751     
40752     
40753     
40754     
40755 };
40756
40757 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40758
40759     setSize : function(width, height){
40760         if(!this.ignoreResize(width, height)){
40761             var size = this.adjustForComponents(width, height);
40762             var el = this.layout.getEl();
40763             if (size.height < 1) {
40764                 el.setWidth(size.width);   
40765             } else {
40766                 el.setSize(size.width, size.height);
40767             }
40768             var touch = el.dom.offsetWidth;
40769             this.layout.layout();
40770             // ie requires a double layout on the first pass
40771             if(Roo.isIE && !this.initialized){
40772                 this.initialized = true;
40773                 this.layout.layout();
40774             }
40775         }
40776     },
40777     
40778     // activate all subpanels if not currently active..
40779     
40780     setActiveState : function(active){
40781         this.active = active;
40782         this.setActiveClass(active);
40783         
40784         if(!active){
40785             this.fireEvent("deactivate", this);
40786             return;
40787         }
40788         
40789         this.fireEvent("activate", this);
40790         // not sure if this should happen before or after..
40791         if (!this.layout) {
40792             return; // should not happen..
40793         }
40794         var reg = false;
40795         for (var r in this.layout.regions) {
40796             reg = this.layout.getRegion(r);
40797             if (reg.getActivePanel()) {
40798                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40799                 reg.setActivePanel(reg.getActivePanel());
40800                 continue;
40801             }
40802             if (!reg.panels.length) {
40803                 continue;
40804             }
40805             reg.showPanel(reg.getPanel(0));
40806         }
40807         
40808         
40809         
40810         
40811     },
40812     
40813     /**
40814      * Returns the nested BorderLayout for this panel
40815      * @return {Roo.BorderLayout} 
40816      */
40817     getLayout : function(){
40818         return this.layout;
40819     },
40820     
40821      /**
40822      * Adds a xtype elements to the layout of the nested panel
40823      * <pre><code>
40824
40825 panel.addxtype({
40826        xtype : 'ContentPanel',
40827        region: 'west',
40828        items: [ .... ]
40829    }
40830 );
40831
40832 panel.addxtype({
40833         xtype : 'NestedLayoutPanel',
40834         region: 'west',
40835         layout: {
40836            center: { },
40837            west: { }   
40838         },
40839         items : [ ... list of content panels or nested layout panels.. ]
40840    }
40841 );
40842 </code></pre>
40843      * @param {Object} cfg Xtype definition of item to add.
40844      */
40845     addxtype : function(cfg) {
40846         return this.layout.addxtype(cfg);
40847     
40848     }
40849 });/*
40850  * Based on:
40851  * Ext JS Library 1.1.1
40852  * Copyright(c) 2006-2007, Ext JS, LLC.
40853  *
40854  * Originally Released Under LGPL - original licence link has changed is not relivant.
40855  *
40856  * Fork - LGPL
40857  * <script type="text/javascript">
40858  */
40859 /**
40860  * @class Roo.TabPanel
40861  * @extends Roo.util.Observable
40862  * A lightweight tab container.
40863  * <br><br>
40864  * Usage:
40865  * <pre><code>
40866 // basic tabs 1, built from existing content
40867 var tabs = new Roo.TabPanel("tabs1");
40868 tabs.addTab("script", "View Script");
40869 tabs.addTab("markup", "View Markup");
40870 tabs.activate("script");
40871
40872 // more advanced tabs, built from javascript
40873 var jtabs = new Roo.TabPanel("jtabs");
40874 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40875
40876 // set up the UpdateManager
40877 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40878 var updater = tab2.getUpdateManager();
40879 updater.setDefaultUrl("ajax1.htm");
40880 tab2.on('activate', updater.refresh, updater, true);
40881
40882 // Use setUrl for Ajax loading
40883 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40884 tab3.setUrl("ajax2.htm", null, true);
40885
40886 // Disabled tab
40887 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40888 tab4.disable();
40889
40890 jtabs.activate("jtabs-1");
40891  * </code></pre>
40892  * @constructor
40893  * Create a new TabPanel.
40894  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40895  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40896  */
40897 Roo.bootstrap.panel.Tabs = function(config){
40898     /**
40899     * The container element for this TabPanel.
40900     * @type Roo.Element
40901     */
40902     this.el = Roo.get(config.el);
40903     delete config.el;
40904     if(config){
40905         if(typeof config == "boolean"){
40906             this.tabPosition = config ? "bottom" : "top";
40907         }else{
40908             Roo.apply(this, config);
40909         }
40910     }
40911     
40912     if(this.tabPosition == "bottom"){
40913         // if tabs are at the bottom = create the body first.
40914         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40915         this.el.addClass("roo-tabs-bottom");
40916     }
40917     // next create the tabs holders
40918     
40919     if (this.tabPosition == "west"){
40920         
40921         var reg = this.region; // fake it..
40922         while (reg) {
40923             if (!reg.mgr.parent) {
40924                 break;
40925             }
40926             reg = reg.mgr.parent.region;
40927         }
40928         Roo.log("got nest?");
40929         Roo.log(reg);
40930         if (reg.mgr.getRegion('west')) {
40931             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40932             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40933             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40934             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40935             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40936         
40937             
40938         }
40939         
40940         
40941     } else {
40942      
40943         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40944         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40945         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40946         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40947     }
40948     
40949     
40950     if(Roo.isIE){
40951         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40952     }
40953     
40954     // finally - if tabs are at the top, then create the body last..
40955     if(this.tabPosition != "bottom"){
40956         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40957          * @type Roo.Element
40958          */
40959         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40960         this.el.addClass("roo-tabs-top");
40961     }
40962     this.items = [];
40963
40964     this.bodyEl.setStyle("position", "relative");
40965
40966     this.active = null;
40967     this.activateDelegate = this.activate.createDelegate(this);
40968
40969     this.addEvents({
40970         /**
40971          * @event tabchange
40972          * Fires when the active tab changes
40973          * @param {Roo.TabPanel} this
40974          * @param {Roo.TabPanelItem} activePanel The new active tab
40975          */
40976         "tabchange": true,
40977         /**
40978          * @event beforetabchange
40979          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40980          * @param {Roo.TabPanel} this
40981          * @param {Object} e Set cancel to true on this object to cancel the tab change
40982          * @param {Roo.TabPanelItem} tab The tab being changed to
40983          */
40984         "beforetabchange" : true
40985     });
40986
40987     Roo.EventManager.onWindowResize(this.onResize, this);
40988     this.cpad = this.el.getPadding("lr");
40989     this.hiddenCount = 0;
40990
40991
40992     // toolbar on the tabbar support...
40993     if (this.toolbar) {
40994         alert("no toolbar support yet");
40995         this.toolbar  = false;
40996         /*
40997         var tcfg = this.toolbar;
40998         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40999         this.toolbar = new Roo.Toolbar(tcfg);
41000         if (Roo.isSafari) {
41001             var tbl = tcfg.container.child('table', true);
41002             tbl.setAttribute('width', '100%');
41003         }
41004         */
41005         
41006     }
41007    
41008
41009
41010     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41011 };
41012
41013 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41014     /*
41015      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41016      */
41017     tabPosition : "top",
41018     /*
41019      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41020      */
41021     currentTabWidth : 0,
41022     /*
41023      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41024      */
41025     minTabWidth : 40,
41026     /*
41027      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41028      */
41029     maxTabWidth : 250,
41030     /*
41031      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41032      */
41033     preferredTabWidth : 175,
41034     /*
41035      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41036      */
41037     resizeTabs : false,
41038     /*
41039      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41040      */
41041     monitorResize : true,
41042     /*
41043      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41044      */
41045     toolbar : false,  // set by caller..
41046     
41047     region : false, /// set by caller
41048     
41049     disableTooltips : true, // not used yet...
41050
41051     /**
41052      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41053      * @param {String} id The id of the div to use <b>or create</b>
41054      * @param {String} text The text for the tab
41055      * @param {String} content (optional) Content to put in the TabPanelItem body
41056      * @param {Boolean} closable (optional) True to create a close icon on the tab
41057      * @return {Roo.TabPanelItem} The created TabPanelItem
41058      */
41059     addTab : function(id, text, content, closable, tpl)
41060     {
41061         var item = new Roo.bootstrap.panel.TabItem({
41062             panel: this,
41063             id : id,
41064             text : text,
41065             closable : closable,
41066             tpl : tpl
41067         });
41068         this.addTabItem(item);
41069         if(content){
41070             item.setContent(content);
41071         }
41072         return item;
41073     },
41074
41075     /**
41076      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41077      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41078      * @return {Roo.TabPanelItem}
41079      */
41080     getTab : function(id){
41081         return this.items[id];
41082     },
41083
41084     /**
41085      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41086      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41087      */
41088     hideTab : function(id){
41089         var t = this.items[id];
41090         if(!t.isHidden()){
41091            t.setHidden(true);
41092            this.hiddenCount++;
41093            this.autoSizeTabs();
41094         }
41095     },
41096
41097     /**
41098      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41099      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41100      */
41101     unhideTab : function(id){
41102         var t = this.items[id];
41103         if(t.isHidden()){
41104            t.setHidden(false);
41105            this.hiddenCount--;
41106            this.autoSizeTabs();
41107         }
41108     },
41109
41110     /**
41111      * Adds an existing {@link Roo.TabPanelItem}.
41112      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41113      */
41114     addTabItem : function(item)
41115     {
41116         this.items[item.id] = item;
41117         this.items.push(item);
41118         this.autoSizeTabs();
41119       //  if(this.resizeTabs){
41120     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41121   //         this.autoSizeTabs();
41122 //        }else{
41123 //            item.autoSize();
41124        // }
41125     },
41126
41127     /**
41128      * Removes a {@link Roo.TabPanelItem}.
41129      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41130      */
41131     removeTab : function(id){
41132         var items = this.items;
41133         var tab = items[id];
41134         if(!tab) { return; }
41135         var index = items.indexOf(tab);
41136         if(this.active == tab && items.length > 1){
41137             var newTab = this.getNextAvailable(index);
41138             if(newTab) {
41139                 newTab.activate();
41140             }
41141         }
41142         this.stripEl.dom.removeChild(tab.pnode.dom);
41143         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41144             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41145         }
41146         items.splice(index, 1);
41147         delete this.items[tab.id];
41148         tab.fireEvent("close", tab);
41149         tab.purgeListeners();
41150         this.autoSizeTabs();
41151     },
41152
41153     getNextAvailable : function(start){
41154         var items = this.items;
41155         var index = start;
41156         // look for a next tab that will slide over to
41157         // replace the one being removed
41158         while(index < items.length){
41159             var item = items[++index];
41160             if(item && !item.isHidden()){
41161                 return item;
41162             }
41163         }
41164         // if one isn't found select the previous tab (on the left)
41165         index = start;
41166         while(index >= 0){
41167             var item = items[--index];
41168             if(item && !item.isHidden()){
41169                 return item;
41170             }
41171         }
41172         return null;
41173     },
41174
41175     /**
41176      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41177      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41178      */
41179     disableTab : function(id){
41180         var tab = this.items[id];
41181         if(tab && this.active != tab){
41182             tab.disable();
41183         }
41184     },
41185
41186     /**
41187      * Enables a {@link Roo.TabPanelItem} that is disabled.
41188      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41189      */
41190     enableTab : function(id){
41191         var tab = this.items[id];
41192         tab.enable();
41193     },
41194
41195     /**
41196      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41197      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41198      * @return {Roo.TabPanelItem} The TabPanelItem.
41199      */
41200     activate : function(id)
41201     {
41202         //Roo.log('activite:'  + id);
41203         
41204         var tab = this.items[id];
41205         if(!tab){
41206             return null;
41207         }
41208         if(tab == this.active || tab.disabled){
41209             return tab;
41210         }
41211         var e = {};
41212         this.fireEvent("beforetabchange", this, e, tab);
41213         if(e.cancel !== true && !tab.disabled){
41214             if(this.active){
41215                 this.active.hide();
41216             }
41217             this.active = this.items[id];
41218             this.active.show();
41219             this.fireEvent("tabchange", this, this.active);
41220         }
41221         return tab;
41222     },
41223
41224     /**
41225      * Gets the active {@link Roo.TabPanelItem}.
41226      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41227      */
41228     getActiveTab : function(){
41229         return this.active;
41230     },
41231
41232     /**
41233      * Updates the tab body element to fit the height of the container element
41234      * for overflow scrolling
41235      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41236      */
41237     syncHeight : function(targetHeight){
41238         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41239         var bm = this.bodyEl.getMargins();
41240         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41241         this.bodyEl.setHeight(newHeight);
41242         return newHeight;
41243     },
41244
41245     onResize : function(){
41246         if(this.monitorResize){
41247             this.autoSizeTabs();
41248         }
41249     },
41250
41251     /**
41252      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41253      */
41254     beginUpdate : function(){
41255         this.updating = true;
41256     },
41257
41258     /**
41259      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41260      */
41261     endUpdate : function(){
41262         this.updating = false;
41263         this.autoSizeTabs();
41264     },
41265
41266     /**
41267      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41268      */
41269     autoSizeTabs : function()
41270     {
41271         var count = this.items.length;
41272         var vcount = count - this.hiddenCount;
41273         
41274         if (vcount < 2) {
41275             this.stripEl.hide();
41276         } else {
41277             this.stripEl.show();
41278         }
41279         
41280         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41281             return;
41282         }
41283         
41284         
41285         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41286         var availWidth = Math.floor(w / vcount);
41287         var b = this.stripBody;
41288         if(b.getWidth() > w){
41289             var tabs = this.items;
41290             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41291             if(availWidth < this.minTabWidth){
41292                 /*if(!this.sleft){    // incomplete scrolling code
41293                     this.createScrollButtons();
41294                 }
41295                 this.showScroll();
41296                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41297             }
41298         }else{
41299             if(this.currentTabWidth < this.preferredTabWidth){
41300                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41301             }
41302         }
41303     },
41304
41305     /**
41306      * Returns the number of tabs in this TabPanel.
41307      * @return {Number}
41308      */
41309      getCount : function(){
41310          return this.items.length;
41311      },
41312
41313     /**
41314      * Resizes all the tabs to the passed width
41315      * @param {Number} The new width
41316      */
41317     setTabWidth : function(width){
41318         this.currentTabWidth = width;
41319         for(var i = 0, len = this.items.length; i < len; i++) {
41320                 if(!this.items[i].isHidden()) {
41321                 this.items[i].setWidth(width);
41322             }
41323         }
41324     },
41325
41326     /**
41327      * Destroys this TabPanel
41328      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41329      */
41330     destroy : function(removeEl){
41331         Roo.EventManager.removeResizeListener(this.onResize, this);
41332         for(var i = 0, len = this.items.length; i < len; i++){
41333             this.items[i].purgeListeners();
41334         }
41335         if(removeEl === true){
41336             this.el.update("");
41337             this.el.remove();
41338         }
41339     },
41340     
41341     createStrip : function(container)
41342     {
41343         var strip = document.createElement("nav");
41344         strip.className = Roo.bootstrap.version == 4 ?
41345             "navbar-light bg-light" : 
41346             "navbar navbar-default"; //"x-tabs-wrap";
41347         container.appendChild(strip);
41348         return strip;
41349     },
41350     
41351     createStripList : function(strip)
41352     {
41353         // div wrapper for retard IE
41354         // returns the "tr" element.
41355         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41356         //'<div class="x-tabs-strip-wrap">'+
41357           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41358           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41359         return strip.firstChild; //.firstChild.firstChild.firstChild;
41360     },
41361     createBody : function(container)
41362     {
41363         var body = document.createElement("div");
41364         Roo.id(body, "tab-body");
41365         //Roo.fly(body).addClass("x-tabs-body");
41366         Roo.fly(body).addClass("tab-content");
41367         container.appendChild(body);
41368         return body;
41369     },
41370     createItemBody :function(bodyEl, id){
41371         var body = Roo.getDom(id);
41372         if(!body){
41373             body = document.createElement("div");
41374             body.id = id;
41375         }
41376         //Roo.fly(body).addClass("x-tabs-item-body");
41377         Roo.fly(body).addClass("tab-pane");
41378          bodyEl.insertBefore(body, bodyEl.firstChild);
41379         return body;
41380     },
41381     /** @private */
41382     createStripElements :  function(stripEl, text, closable, tpl)
41383     {
41384         var td = document.createElement("li"); // was td..
41385         td.className = 'nav-item';
41386         
41387         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41388         
41389         
41390         stripEl.appendChild(td);
41391         /*if(closable){
41392             td.className = "x-tabs-closable";
41393             if(!this.closeTpl){
41394                 this.closeTpl = new Roo.Template(
41395                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41396                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41397                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41398                 );
41399             }
41400             var el = this.closeTpl.overwrite(td, {"text": text});
41401             var close = el.getElementsByTagName("div")[0];
41402             var inner = el.getElementsByTagName("em")[0];
41403             return {"el": el, "close": close, "inner": inner};
41404         } else {
41405         */
41406         // not sure what this is..
41407 //            if(!this.tabTpl){
41408                 //this.tabTpl = new Roo.Template(
41409                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41410                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41411                 //);
41412 //                this.tabTpl = new Roo.Template(
41413 //                   '<a href="#">' +
41414 //                   '<span unselectable="on"' +
41415 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41416 //                            ' >{text}</span></a>'
41417 //                );
41418 //                
41419 //            }
41420
41421
41422             var template = tpl || this.tabTpl || false;
41423             
41424             if(!template){
41425                 template =  new Roo.Template(
41426                         Roo.bootstrap.version == 4 ? 
41427                             (
41428                                 '<a class="nav-link" href="#" unselectable="on"' +
41429                                      (this.disableTooltips ? '' : ' title="{text}"') +
41430                                      ' >{text}</a>'
41431                             ) : (
41432                                 '<a class="nav-link" href="#">' +
41433                                 '<span unselectable="on"' +
41434                                          (this.disableTooltips ? '' : ' title="{text}"') +
41435                                     ' >{text}</span></a>'
41436                             )
41437                 );
41438             }
41439             
41440             switch (typeof(template)) {
41441                 case 'object' :
41442                     break;
41443                 case 'string' :
41444                     template = new Roo.Template(template);
41445                     break;
41446                 default :
41447                     break;
41448             }
41449             
41450             var el = template.overwrite(td, {"text": text});
41451             
41452             var inner = el.getElementsByTagName("span")[0];
41453             
41454             return {"el": el, "inner": inner};
41455             
41456     }
41457         
41458     
41459 });
41460
41461 /**
41462  * @class Roo.TabPanelItem
41463  * @extends Roo.util.Observable
41464  * Represents an individual item (tab plus body) in a TabPanel.
41465  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41466  * @param {String} id The id of this TabPanelItem
41467  * @param {String} text The text for the tab of this TabPanelItem
41468  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41469  */
41470 Roo.bootstrap.panel.TabItem = function(config){
41471     /**
41472      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41473      * @type Roo.TabPanel
41474      */
41475     this.tabPanel = config.panel;
41476     /**
41477      * The id for this TabPanelItem
41478      * @type String
41479      */
41480     this.id = config.id;
41481     /** @private */
41482     this.disabled = false;
41483     /** @private */
41484     this.text = config.text;
41485     /** @private */
41486     this.loaded = false;
41487     this.closable = config.closable;
41488
41489     /**
41490      * The body element for this TabPanelItem.
41491      * @type Roo.Element
41492      */
41493     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41494     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41495     this.bodyEl.setStyle("display", "block");
41496     this.bodyEl.setStyle("zoom", "1");
41497     //this.hideAction();
41498
41499     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41500     /** @private */
41501     this.el = Roo.get(els.el);
41502     this.inner = Roo.get(els.inner, true);
41503      this.textEl = Roo.bootstrap.version == 4 ?
41504         this.el : Roo.get(this.el.dom.firstChild, true);
41505
41506     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41507     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41508
41509     
41510 //    this.el.on("mousedown", this.onTabMouseDown, this);
41511     this.el.on("click", this.onTabClick, this);
41512     /** @private */
41513     if(config.closable){
41514         var c = Roo.get(els.close, true);
41515         c.dom.title = this.closeText;
41516         c.addClassOnOver("close-over");
41517         c.on("click", this.closeClick, this);
41518      }
41519
41520     this.addEvents({
41521          /**
41522          * @event activate
41523          * Fires when this tab becomes the active tab.
41524          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41525          * @param {Roo.TabPanelItem} this
41526          */
41527         "activate": true,
41528         /**
41529          * @event beforeclose
41530          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41531          * @param {Roo.TabPanelItem} this
41532          * @param {Object} e Set cancel to true on this object to cancel the close.
41533          */
41534         "beforeclose": true,
41535         /**
41536          * @event close
41537          * Fires when this tab is closed.
41538          * @param {Roo.TabPanelItem} this
41539          */
41540          "close": true,
41541         /**
41542          * @event deactivate
41543          * Fires when this tab is no longer the active tab.
41544          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41545          * @param {Roo.TabPanelItem} this
41546          */
41547          "deactivate" : true
41548     });
41549     this.hidden = false;
41550
41551     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41552 };
41553
41554 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41555            {
41556     purgeListeners : function(){
41557        Roo.util.Observable.prototype.purgeListeners.call(this);
41558        this.el.removeAllListeners();
41559     },
41560     /**
41561      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41562      */
41563     show : function(){
41564         this.status_node.addClass("active");
41565         this.showAction();
41566         if(Roo.isOpera){
41567             this.tabPanel.stripWrap.repaint();
41568         }
41569         this.fireEvent("activate", this.tabPanel, this);
41570     },
41571
41572     /**
41573      * Returns true if this tab is the active tab.
41574      * @return {Boolean}
41575      */
41576     isActive : function(){
41577         return this.tabPanel.getActiveTab() == this;
41578     },
41579
41580     /**
41581      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41582      */
41583     hide : function(){
41584         this.status_node.removeClass("active");
41585         this.hideAction();
41586         this.fireEvent("deactivate", this.tabPanel, this);
41587     },
41588
41589     hideAction : function(){
41590         this.bodyEl.hide();
41591         this.bodyEl.setStyle("position", "absolute");
41592         this.bodyEl.setLeft("-20000px");
41593         this.bodyEl.setTop("-20000px");
41594     },
41595
41596     showAction : function(){
41597         this.bodyEl.setStyle("position", "relative");
41598         this.bodyEl.setTop("");
41599         this.bodyEl.setLeft("");
41600         this.bodyEl.show();
41601     },
41602
41603     /**
41604      * Set the tooltip for the tab.
41605      * @param {String} tooltip The tab's tooltip
41606      */
41607     setTooltip : function(text){
41608         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41609             this.textEl.dom.qtip = text;
41610             this.textEl.dom.removeAttribute('title');
41611         }else{
41612             this.textEl.dom.title = text;
41613         }
41614     },
41615
41616     onTabClick : function(e){
41617         e.preventDefault();
41618         this.tabPanel.activate(this.id);
41619     },
41620
41621     onTabMouseDown : function(e){
41622         e.preventDefault();
41623         this.tabPanel.activate(this.id);
41624     },
41625 /*
41626     getWidth : function(){
41627         return this.inner.getWidth();
41628     },
41629
41630     setWidth : function(width){
41631         var iwidth = width - this.linode.getPadding("lr");
41632         this.inner.setWidth(iwidth);
41633         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41634         this.linode.setWidth(width);
41635     },
41636 */
41637     /**
41638      * Show or hide the tab
41639      * @param {Boolean} hidden True to hide or false to show.
41640      */
41641     setHidden : function(hidden){
41642         this.hidden = hidden;
41643         this.linode.setStyle("display", hidden ? "none" : "");
41644     },
41645
41646     /**
41647      * Returns true if this tab is "hidden"
41648      * @return {Boolean}
41649      */
41650     isHidden : function(){
41651         return this.hidden;
41652     },
41653
41654     /**
41655      * Returns the text for this tab
41656      * @return {String}
41657      */
41658     getText : function(){
41659         return this.text;
41660     },
41661     /*
41662     autoSize : function(){
41663         //this.el.beginMeasure();
41664         this.textEl.setWidth(1);
41665         /*
41666          *  #2804 [new] Tabs in Roojs
41667          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41668          */
41669         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41670         //this.el.endMeasure();
41671     //},
41672
41673     /**
41674      * Sets the text for the tab (Note: this also sets the tooltip text)
41675      * @param {String} text The tab's text and tooltip
41676      */
41677     setText : function(text){
41678         this.text = text;
41679         this.textEl.update(text);
41680         this.setTooltip(text);
41681         //if(!this.tabPanel.resizeTabs){
41682         //    this.autoSize();
41683         //}
41684     },
41685     /**
41686      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41687      */
41688     activate : function(){
41689         this.tabPanel.activate(this.id);
41690     },
41691
41692     /**
41693      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41694      */
41695     disable : function(){
41696         if(this.tabPanel.active != this){
41697             this.disabled = true;
41698             this.status_node.addClass("disabled");
41699         }
41700     },
41701
41702     /**
41703      * Enables this TabPanelItem if it was previously disabled.
41704      */
41705     enable : function(){
41706         this.disabled = false;
41707         this.status_node.removeClass("disabled");
41708     },
41709
41710     /**
41711      * Sets the content for this TabPanelItem.
41712      * @param {String} content The content
41713      * @param {Boolean} loadScripts true to look for and load scripts
41714      */
41715     setContent : function(content, loadScripts){
41716         this.bodyEl.update(content, loadScripts);
41717     },
41718
41719     /**
41720      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41721      * @return {Roo.UpdateManager} The UpdateManager
41722      */
41723     getUpdateManager : function(){
41724         return this.bodyEl.getUpdateManager();
41725     },
41726
41727     /**
41728      * Set a URL to be used to load the content for this TabPanelItem.
41729      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41730      * @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)
41731      * @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)
41732      * @return {Roo.UpdateManager} The UpdateManager
41733      */
41734     setUrl : function(url, params, loadOnce){
41735         if(this.refreshDelegate){
41736             this.un('activate', this.refreshDelegate);
41737         }
41738         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41739         this.on("activate", this.refreshDelegate);
41740         return this.bodyEl.getUpdateManager();
41741     },
41742
41743     /** @private */
41744     _handleRefresh : function(url, params, loadOnce){
41745         if(!loadOnce || !this.loaded){
41746             var updater = this.bodyEl.getUpdateManager();
41747             updater.update(url, params, this._setLoaded.createDelegate(this));
41748         }
41749     },
41750
41751     /**
41752      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41753      *   Will fail silently if the setUrl method has not been called.
41754      *   This does not activate the panel, just updates its content.
41755      */
41756     refresh : function(){
41757         if(this.refreshDelegate){
41758            this.loaded = false;
41759            this.refreshDelegate();
41760         }
41761     },
41762
41763     /** @private */
41764     _setLoaded : function(){
41765         this.loaded = true;
41766     },
41767
41768     /** @private */
41769     closeClick : function(e){
41770         var o = {};
41771         e.stopEvent();
41772         this.fireEvent("beforeclose", this, o);
41773         if(o.cancel !== true){
41774             this.tabPanel.removeTab(this.id);
41775         }
41776     },
41777     /**
41778      * The text displayed in the tooltip for the close icon.
41779      * @type String
41780      */
41781     closeText : "Close this tab"
41782 });
41783 /**
41784 *    This script refer to:
41785 *    Title: International Telephone Input
41786 *    Author: Jack O'Connor
41787 *    Code version:  v12.1.12
41788 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41789 **/
41790
41791 Roo.bootstrap.PhoneInputData = function() {
41792     var d = [
41793       [
41794         "Afghanistan (‫افغانستان‬‎)",
41795         "af",
41796         "93"
41797       ],
41798       [
41799         "Albania (Shqipëri)",
41800         "al",
41801         "355"
41802       ],
41803       [
41804         "Algeria (‫الجزائر‬‎)",
41805         "dz",
41806         "213"
41807       ],
41808       [
41809         "American Samoa",
41810         "as",
41811         "1684"
41812       ],
41813       [
41814         "Andorra",
41815         "ad",
41816         "376"
41817       ],
41818       [
41819         "Angola",
41820         "ao",
41821         "244"
41822       ],
41823       [
41824         "Anguilla",
41825         "ai",
41826         "1264"
41827       ],
41828       [
41829         "Antigua and Barbuda",
41830         "ag",
41831         "1268"
41832       ],
41833       [
41834         "Argentina",
41835         "ar",
41836         "54"
41837       ],
41838       [
41839         "Armenia (Հայաստան)",
41840         "am",
41841         "374"
41842       ],
41843       [
41844         "Aruba",
41845         "aw",
41846         "297"
41847       ],
41848       [
41849         "Australia",
41850         "au",
41851         "61",
41852         0
41853       ],
41854       [
41855         "Austria (Österreich)",
41856         "at",
41857         "43"
41858       ],
41859       [
41860         "Azerbaijan (Azərbaycan)",
41861         "az",
41862         "994"
41863       ],
41864       [
41865         "Bahamas",
41866         "bs",
41867         "1242"
41868       ],
41869       [
41870         "Bahrain (‫البحرين‬‎)",
41871         "bh",
41872         "973"
41873       ],
41874       [
41875         "Bangladesh (বাংলাদেশ)",
41876         "bd",
41877         "880"
41878       ],
41879       [
41880         "Barbados",
41881         "bb",
41882         "1246"
41883       ],
41884       [
41885         "Belarus (Беларусь)",
41886         "by",
41887         "375"
41888       ],
41889       [
41890         "Belgium (België)",
41891         "be",
41892         "32"
41893       ],
41894       [
41895         "Belize",
41896         "bz",
41897         "501"
41898       ],
41899       [
41900         "Benin (Bénin)",
41901         "bj",
41902         "229"
41903       ],
41904       [
41905         "Bermuda",
41906         "bm",
41907         "1441"
41908       ],
41909       [
41910         "Bhutan (འབྲུག)",
41911         "bt",
41912         "975"
41913       ],
41914       [
41915         "Bolivia",
41916         "bo",
41917         "591"
41918       ],
41919       [
41920         "Bosnia and Herzegovina (Босна и Херцеговина)",
41921         "ba",
41922         "387"
41923       ],
41924       [
41925         "Botswana",
41926         "bw",
41927         "267"
41928       ],
41929       [
41930         "Brazil (Brasil)",
41931         "br",
41932         "55"
41933       ],
41934       [
41935         "British Indian Ocean Territory",
41936         "io",
41937         "246"
41938       ],
41939       [
41940         "British Virgin Islands",
41941         "vg",
41942         "1284"
41943       ],
41944       [
41945         "Brunei",
41946         "bn",
41947         "673"
41948       ],
41949       [
41950         "Bulgaria (България)",
41951         "bg",
41952         "359"
41953       ],
41954       [
41955         "Burkina Faso",
41956         "bf",
41957         "226"
41958       ],
41959       [
41960         "Burundi (Uburundi)",
41961         "bi",
41962         "257"
41963       ],
41964       [
41965         "Cambodia (កម្ពុជា)",
41966         "kh",
41967         "855"
41968       ],
41969       [
41970         "Cameroon (Cameroun)",
41971         "cm",
41972         "237"
41973       ],
41974       [
41975         "Canada",
41976         "ca",
41977         "1",
41978         1,
41979         ["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"]
41980       ],
41981       [
41982         "Cape Verde (Kabu Verdi)",
41983         "cv",
41984         "238"
41985       ],
41986       [
41987         "Caribbean Netherlands",
41988         "bq",
41989         "599",
41990         1
41991       ],
41992       [
41993         "Cayman Islands",
41994         "ky",
41995         "1345"
41996       ],
41997       [
41998         "Central African Republic (République centrafricaine)",
41999         "cf",
42000         "236"
42001       ],
42002       [
42003         "Chad (Tchad)",
42004         "td",
42005         "235"
42006       ],
42007       [
42008         "Chile",
42009         "cl",
42010         "56"
42011       ],
42012       [
42013         "China (中国)",
42014         "cn",
42015         "86"
42016       ],
42017       [
42018         "Christmas Island",
42019         "cx",
42020         "61",
42021         2
42022       ],
42023       [
42024         "Cocos (Keeling) Islands",
42025         "cc",
42026         "61",
42027         1
42028       ],
42029       [
42030         "Colombia",
42031         "co",
42032         "57"
42033       ],
42034       [
42035         "Comoros (‫جزر القمر‬‎)",
42036         "km",
42037         "269"
42038       ],
42039       [
42040         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42041         "cd",
42042         "243"
42043       ],
42044       [
42045         "Congo (Republic) (Congo-Brazzaville)",
42046         "cg",
42047         "242"
42048       ],
42049       [
42050         "Cook Islands",
42051         "ck",
42052         "682"
42053       ],
42054       [
42055         "Costa Rica",
42056         "cr",
42057         "506"
42058       ],
42059       [
42060         "Côte d’Ivoire",
42061         "ci",
42062         "225"
42063       ],
42064       [
42065         "Croatia (Hrvatska)",
42066         "hr",
42067         "385"
42068       ],
42069       [
42070         "Cuba",
42071         "cu",
42072         "53"
42073       ],
42074       [
42075         "Curaçao",
42076         "cw",
42077         "599",
42078         0
42079       ],
42080       [
42081         "Cyprus (Κύπρος)",
42082         "cy",
42083         "357"
42084       ],
42085       [
42086         "Czech Republic (Česká republika)",
42087         "cz",
42088         "420"
42089       ],
42090       [
42091         "Denmark (Danmark)",
42092         "dk",
42093         "45"
42094       ],
42095       [
42096         "Djibouti",
42097         "dj",
42098         "253"
42099       ],
42100       [
42101         "Dominica",
42102         "dm",
42103         "1767"
42104       ],
42105       [
42106         "Dominican Republic (República Dominicana)",
42107         "do",
42108         "1",
42109         2,
42110         ["809", "829", "849"]
42111       ],
42112       [
42113         "Ecuador",
42114         "ec",
42115         "593"
42116       ],
42117       [
42118         "Egypt (‫مصر‬‎)",
42119         "eg",
42120         "20"
42121       ],
42122       [
42123         "El Salvador",
42124         "sv",
42125         "503"
42126       ],
42127       [
42128         "Equatorial Guinea (Guinea Ecuatorial)",
42129         "gq",
42130         "240"
42131       ],
42132       [
42133         "Eritrea",
42134         "er",
42135         "291"
42136       ],
42137       [
42138         "Estonia (Eesti)",
42139         "ee",
42140         "372"
42141       ],
42142       [
42143         "Ethiopia",
42144         "et",
42145         "251"
42146       ],
42147       [
42148         "Falkland Islands (Islas Malvinas)",
42149         "fk",
42150         "500"
42151       ],
42152       [
42153         "Faroe Islands (Føroyar)",
42154         "fo",
42155         "298"
42156       ],
42157       [
42158         "Fiji",
42159         "fj",
42160         "679"
42161       ],
42162       [
42163         "Finland (Suomi)",
42164         "fi",
42165         "358",
42166         0
42167       ],
42168       [
42169         "France",
42170         "fr",
42171         "33"
42172       ],
42173       [
42174         "French Guiana (Guyane française)",
42175         "gf",
42176         "594"
42177       ],
42178       [
42179         "French Polynesia (Polynésie française)",
42180         "pf",
42181         "689"
42182       ],
42183       [
42184         "Gabon",
42185         "ga",
42186         "241"
42187       ],
42188       [
42189         "Gambia",
42190         "gm",
42191         "220"
42192       ],
42193       [
42194         "Georgia (საქართველო)",
42195         "ge",
42196         "995"
42197       ],
42198       [
42199         "Germany (Deutschland)",
42200         "de",
42201         "49"
42202       ],
42203       [
42204         "Ghana (Gaana)",
42205         "gh",
42206         "233"
42207       ],
42208       [
42209         "Gibraltar",
42210         "gi",
42211         "350"
42212       ],
42213       [
42214         "Greece (Ελλάδα)",
42215         "gr",
42216         "30"
42217       ],
42218       [
42219         "Greenland (Kalaallit Nunaat)",
42220         "gl",
42221         "299"
42222       ],
42223       [
42224         "Grenada",
42225         "gd",
42226         "1473"
42227       ],
42228       [
42229         "Guadeloupe",
42230         "gp",
42231         "590",
42232         0
42233       ],
42234       [
42235         "Guam",
42236         "gu",
42237         "1671"
42238       ],
42239       [
42240         "Guatemala",
42241         "gt",
42242         "502"
42243       ],
42244       [
42245         "Guernsey",
42246         "gg",
42247         "44",
42248         1
42249       ],
42250       [
42251         "Guinea (Guinée)",
42252         "gn",
42253         "224"
42254       ],
42255       [
42256         "Guinea-Bissau (Guiné Bissau)",
42257         "gw",
42258         "245"
42259       ],
42260       [
42261         "Guyana",
42262         "gy",
42263         "592"
42264       ],
42265       [
42266         "Haiti",
42267         "ht",
42268         "509"
42269       ],
42270       [
42271         "Honduras",
42272         "hn",
42273         "504"
42274       ],
42275       [
42276         "Hong Kong (香港)",
42277         "hk",
42278         "852"
42279       ],
42280       [
42281         "Hungary (Magyarország)",
42282         "hu",
42283         "36"
42284       ],
42285       [
42286         "Iceland (Ísland)",
42287         "is",
42288         "354"
42289       ],
42290       [
42291         "India (भारत)",
42292         "in",
42293         "91"
42294       ],
42295       [
42296         "Indonesia",
42297         "id",
42298         "62"
42299       ],
42300       [
42301         "Iran (‫ایران‬‎)",
42302         "ir",
42303         "98"
42304       ],
42305       [
42306         "Iraq (‫العراق‬‎)",
42307         "iq",
42308         "964"
42309       ],
42310       [
42311         "Ireland",
42312         "ie",
42313         "353"
42314       ],
42315       [
42316         "Isle of Man",
42317         "im",
42318         "44",
42319         2
42320       ],
42321       [
42322         "Israel (‫ישראל‬‎)",
42323         "il",
42324         "972"
42325       ],
42326       [
42327         "Italy (Italia)",
42328         "it",
42329         "39",
42330         0
42331       ],
42332       [
42333         "Jamaica",
42334         "jm",
42335         "1876"
42336       ],
42337       [
42338         "Japan (日本)",
42339         "jp",
42340         "81"
42341       ],
42342       [
42343         "Jersey",
42344         "je",
42345         "44",
42346         3
42347       ],
42348       [
42349         "Jordan (‫الأردن‬‎)",
42350         "jo",
42351         "962"
42352       ],
42353       [
42354         "Kazakhstan (Казахстан)",
42355         "kz",
42356         "7",
42357         1
42358       ],
42359       [
42360         "Kenya",
42361         "ke",
42362         "254"
42363       ],
42364       [
42365         "Kiribati",
42366         "ki",
42367         "686"
42368       ],
42369       [
42370         "Kosovo",
42371         "xk",
42372         "383"
42373       ],
42374       [
42375         "Kuwait (‫الكويت‬‎)",
42376         "kw",
42377         "965"
42378       ],
42379       [
42380         "Kyrgyzstan (Кыргызстан)",
42381         "kg",
42382         "996"
42383       ],
42384       [
42385         "Laos (ລາວ)",
42386         "la",
42387         "856"
42388       ],
42389       [
42390         "Latvia (Latvija)",
42391         "lv",
42392         "371"
42393       ],
42394       [
42395         "Lebanon (‫لبنان‬‎)",
42396         "lb",
42397         "961"
42398       ],
42399       [
42400         "Lesotho",
42401         "ls",
42402         "266"
42403       ],
42404       [
42405         "Liberia",
42406         "lr",
42407         "231"
42408       ],
42409       [
42410         "Libya (‫ليبيا‬‎)",
42411         "ly",
42412         "218"
42413       ],
42414       [
42415         "Liechtenstein",
42416         "li",
42417         "423"
42418       ],
42419       [
42420         "Lithuania (Lietuva)",
42421         "lt",
42422         "370"
42423       ],
42424       [
42425         "Luxembourg",
42426         "lu",
42427         "352"
42428       ],
42429       [
42430         "Macau (澳門)",
42431         "mo",
42432         "853"
42433       ],
42434       [
42435         "Macedonia (FYROM) (Македонија)",
42436         "mk",
42437         "389"
42438       ],
42439       [
42440         "Madagascar (Madagasikara)",
42441         "mg",
42442         "261"
42443       ],
42444       [
42445         "Malawi",
42446         "mw",
42447         "265"
42448       ],
42449       [
42450         "Malaysia",
42451         "my",
42452         "60"
42453       ],
42454       [
42455         "Maldives",
42456         "mv",
42457         "960"
42458       ],
42459       [
42460         "Mali",
42461         "ml",
42462         "223"
42463       ],
42464       [
42465         "Malta",
42466         "mt",
42467         "356"
42468       ],
42469       [
42470         "Marshall Islands",
42471         "mh",
42472         "692"
42473       ],
42474       [
42475         "Martinique",
42476         "mq",
42477         "596"
42478       ],
42479       [
42480         "Mauritania (‫موريتانيا‬‎)",
42481         "mr",
42482         "222"
42483       ],
42484       [
42485         "Mauritius (Moris)",
42486         "mu",
42487         "230"
42488       ],
42489       [
42490         "Mayotte",
42491         "yt",
42492         "262",
42493         1
42494       ],
42495       [
42496         "Mexico (México)",
42497         "mx",
42498         "52"
42499       ],
42500       [
42501         "Micronesia",
42502         "fm",
42503         "691"
42504       ],
42505       [
42506         "Moldova (Republica Moldova)",
42507         "md",
42508         "373"
42509       ],
42510       [
42511         "Monaco",
42512         "mc",
42513         "377"
42514       ],
42515       [
42516         "Mongolia (Монгол)",
42517         "mn",
42518         "976"
42519       ],
42520       [
42521         "Montenegro (Crna Gora)",
42522         "me",
42523         "382"
42524       ],
42525       [
42526         "Montserrat",
42527         "ms",
42528         "1664"
42529       ],
42530       [
42531         "Morocco (‫المغرب‬‎)",
42532         "ma",
42533         "212",
42534         0
42535       ],
42536       [
42537         "Mozambique (Moçambique)",
42538         "mz",
42539         "258"
42540       ],
42541       [
42542         "Myanmar (Burma) (မြန်မာ)",
42543         "mm",
42544         "95"
42545       ],
42546       [
42547         "Namibia (Namibië)",
42548         "na",
42549         "264"
42550       ],
42551       [
42552         "Nauru",
42553         "nr",
42554         "674"
42555       ],
42556       [
42557         "Nepal (नेपाल)",
42558         "np",
42559         "977"
42560       ],
42561       [
42562         "Netherlands (Nederland)",
42563         "nl",
42564         "31"
42565       ],
42566       [
42567         "New Caledonia (Nouvelle-Calédonie)",
42568         "nc",
42569         "687"
42570       ],
42571       [
42572         "New Zealand",
42573         "nz",
42574         "64"
42575       ],
42576       [
42577         "Nicaragua",
42578         "ni",
42579         "505"
42580       ],
42581       [
42582         "Niger (Nijar)",
42583         "ne",
42584         "227"
42585       ],
42586       [
42587         "Nigeria",
42588         "ng",
42589         "234"
42590       ],
42591       [
42592         "Niue",
42593         "nu",
42594         "683"
42595       ],
42596       [
42597         "Norfolk Island",
42598         "nf",
42599         "672"
42600       ],
42601       [
42602         "North Korea (조선 민주주의 인민 공화국)",
42603         "kp",
42604         "850"
42605       ],
42606       [
42607         "Northern Mariana Islands",
42608         "mp",
42609         "1670"
42610       ],
42611       [
42612         "Norway (Norge)",
42613         "no",
42614         "47",
42615         0
42616       ],
42617       [
42618         "Oman (‫عُمان‬‎)",
42619         "om",
42620         "968"
42621       ],
42622       [
42623         "Pakistan (‫پاکستان‬‎)",
42624         "pk",
42625         "92"
42626       ],
42627       [
42628         "Palau",
42629         "pw",
42630         "680"
42631       ],
42632       [
42633         "Palestine (‫فلسطين‬‎)",
42634         "ps",
42635         "970"
42636       ],
42637       [
42638         "Panama (Panamá)",
42639         "pa",
42640         "507"
42641       ],
42642       [
42643         "Papua New Guinea",
42644         "pg",
42645         "675"
42646       ],
42647       [
42648         "Paraguay",
42649         "py",
42650         "595"
42651       ],
42652       [
42653         "Peru (Perú)",
42654         "pe",
42655         "51"
42656       ],
42657       [
42658         "Philippines",
42659         "ph",
42660         "63"
42661       ],
42662       [
42663         "Poland (Polska)",
42664         "pl",
42665         "48"
42666       ],
42667       [
42668         "Portugal",
42669         "pt",
42670         "351"
42671       ],
42672       [
42673         "Puerto Rico",
42674         "pr",
42675         "1",
42676         3,
42677         ["787", "939"]
42678       ],
42679       [
42680         "Qatar (‫قطر‬‎)",
42681         "qa",
42682         "974"
42683       ],
42684       [
42685         "Réunion (La Réunion)",
42686         "re",
42687         "262",
42688         0
42689       ],
42690       [
42691         "Romania (România)",
42692         "ro",
42693         "40"
42694       ],
42695       [
42696         "Russia (Россия)",
42697         "ru",
42698         "7",
42699         0
42700       ],
42701       [
42702         "Rwanda",
42703         "rw",
42704         "250"
42705       ],
42706       [
42707         "Saint Barthélemy",
42708         "bl",
42709         "590",
42710         1
42711       ],
42712       [
42713         "Saint Helena",
42714         "sh",
42715         "290"
42716       ],
42717       [
42718         "Saint Kitts and Nevis",
42719         "kn",
42720         "1869"
42721       ],
42722       [
42723         "Saint Lucia",
42724         "lc",
42725         "1758"
42726       ],
42727       [
42728         "Saint Martin (Saint-Martin (partie française))",
42729         "mf",
42730         "590",
42731         2
42732       ],
42733       [
42734         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42735         "pm",
42736         "508"
42737       ],
42738       [
42739         "Saint Vincent and the Grenadines",
42740         "vc",
42741         "1784"
42742       ],
42743       [
42744         "Samoa",
42745         "ws",
42746         "685"
42747       ],
42748       [
42749         "San Marino",
42750         "sm",
42751         "378"
42752       ],
42753       [
42754         "São Tomé and Príncipe (São Tomé e Príncipe)",
42755         "st",
42756         "239"
42757       ],
42758       [
42759         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42760         "sa",
42761         "966"
42762       ],
42763       [
42764         "Senegal (Sénégal)",
42765         "sn",
42766         "221"
42767       ],
42768       [
42769         "Serbia (Србија)",
42770         "rs",
42771         "381"
42772       ],
42773       [
42774         "Seychelles",
42775         "sc",
42776         "248"
42777       ],
42778       [
42779         "Sierra Leone",
42780         "sl",
42781         "232"
42782       ],
42783       [
42784         "Singapore",
42785         "sg",
42786         "65"
42787       ],
42788       [
42789         "Sint Maarten",
42790         "sx",
42791         "1721"
42792       ],
42793       [
42794         "Slovakia (Slovensko)",
42795         "sk",
42796         "421"
42797       ],
42798       [
42799         "Slovenia (Slovenija)",
42800         "si",
42801         "386"
42802       ],
42803       [
42804         "Solomon Islands",
42805         "sb",
42806         "677"
42807       ],
42808       [
42809         "Somalia (Soomaaliya)",
42810         "so",
42811         "252"
42812       ],
42813       [
42814         "South Africa",
42815         "za",
42816         "27"
42817       ],
42818       [
42819         "South Korea (대한민국)",
42820         "kr",
42821         "82"
42822       ],
42823       [
42824         "South Sudan (‫جنوب السودان‬‎)",
42825         "ss",
42826         "211"
42827       ],
42828       [
42829         "Spain (España)",
42830         "es",
42831         "34"
42832       ],
42833       [
42834         "Sri Lanka (ශ්‍රී ලංකාව)",
42835         "lk",
42836         "94"
42837       ],
42838       [
42839         "Sudan (‫السودان‬‎)",
42840         "sd",
42841         "249"
42842       ],
42843       [
42844         "Suriname",
42845         "sr",
42846         "597"
42847       ],
42848       [
42849         "Svalbard and Jan Mayen",
42850         "sj",
42851         "47",
42852         1
42853       ],
42854       [
42855         "Swaziland",
42856         "sz",
42857         "268"
42858       ],
42859       [
42860         "Sweden (Sverige)",
42861         "se",
42862         "46"
42863       ],
42864       [
42865         "Switzerland (Schweiz)",
42866         "ch",
42867         "41"
42868       ],
42869       [
42870         "Syria (‫سوريا‬‎)",
42871         "sy",
42872         "963"
42873       ],
42874       [
42875         "Taiwan (台灣)",
42876         "tw",
42877         "886"
42878       ],
42879       [
42880         "Tajikistan",
42881         "tj",
42882         "992"
42883       ],
42884       [
42885         "Tanzania",
42886         "tz",
42887         "255"
42888       ],
42889       [
42890         "Thailand (ไทย)",
42891         "th",
42892         "66"
42893       ],
42894       [
42895         "Timor-Leste",
42896         "tl",
42897         "670"
42898       ],
42899       [
42900         "Togo",
42901         "tg",
42902         "228"
42903       ],
42904       [
42905         "Tokelau",
42906         "tk",
42907         "690"
42908       ],
42909       [
42910         "Tonga",
42911         "to",
42912         "676"
42913       ],
42914       [
42915         "Trinidad and Tobago",
42916         "tt",
42917         "1868"
42918       ],
42919       [
42920         "Tunisia (‫تونس‬‎)",
42921         "tn",
42922         "216"
42923       ],
42924       [
42925         "Turkey (Türkiye)",
42926         "tr",
42927         "90"
42928       ],
42929       [
42930         "Turkmenistan",
42931         "tm",
42932         "993"
42933       ],
42934       [
42935         "Turks and Caicos Islands",
42936         "tc",
42937         "1649"
42938       ],
42939       [
42940         "Tuvalu",
42941         "tv",
42942         "688"
42943       ],
42944       [
42945         "U.S. Virgin Islands",
42946         "vi",
42947         "1340"
42948       ],
42949       [
42950         "Uganda",
42951         "ug",
42952         "256"
42953       ],
42954       [
42955         "Ukraine (Україна)",
42956         "ua",
42957         "380"
42958       ],
42959       [
42960         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42961         "ae",
42962         "971"
42963       ],
42964       [
42965         "United Kingdom",
42966         "gb",
42967         "44",
42968         0
42969       ],
42970       [
42971         "United States",
42972         "us",
42973         "1",
42974         0
42975       ],
42976       [
42977         "Uruguay",
42978         "uy",
42979         "598"
42980       ],
42981       [
42982         "Uzbekistan (Oʻzbekiston)",
42983         "uz",
42984         "998"
42985       ],
42986       [
42987         "Vanuatu",
42988         "vu",
42989         "678"
42990       ],
42991       [
42992         "Vatican City (Città del Vaticano)",
42993         "va",
42994         "39",
42995         1
42996       ],
42997       [
42998         "Venezuela",
42999         "ve",
43000         "58"
43001       ],
43002       [
43003         "Vietnam (Việt Nam)",
43004         "vn",
43005         "84"
43006       ],
43007       [
43008         "Wallis and Futuna (Wallis-et-Futuna)",
43009         "wf",
43010         "681"
43011       ],
43012       [
43013         "Western Sahara (‫الصحراء الغربية‬‎)",
43014         "eh",
43015         "212",
43016         1
43017       ],
43018       [
43019         "Yemen (‫اليمن‬‎)",
43020         "ye",
43021         "967"
43022       ],
43023       [
43024         "Zambia",
43025         "zm",
43026         "260"
43027       ],
43028       [
43029         "Zimbabwe",
43030         "zw",
43031         "263"
43032       ],
43033       [
43034         "Åland Islands",
43035         "ax",
43036         "358",
43037         1
43038       ]
43039   ];
43040   
43041   return d;
43042 }/**
43043 *    This script refer to:
43044 *    Title: International Telephone Input
43045 *    Author: Jack O'Connor
43046 *    Code version:  v12.1.12
43047 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43048 **/
43049
43050 /**
43051  * @class Roo.bootstrap.PhoneInput
43052  * @extends Roo.bootstrap.TriggerField
43053  * An input with International dial-code selection
43054  
43055  * @cfg {String} defaultDialCode default '+852'
43056  * @cfg {Array} preferedCountries default []
43057   
43058  * @constructor
43059  * Create a new PhoneInput.
43060  * @param {Object} config Configuration options
43061  */
43062
43063 Roo.bootstrap.PhoneInput = function(config) {
43064     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43065 };
43066
43067 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43068         
43069         listWidth: undefined,
43070         
43071         selectedClass: 'active',
43072         
43073         invalidClass : "has-warning",
43074         
43075         validClass: 'has-success',
43076         
43077         allowed: '0123456789',
43078         
43079         max_length: 15,
43080         
43081         /**
43082          * @cfg {String} defaultDialCode The default dial code when initializing the input
43083          */
43084         defaultDialCode: '+852',
43085         
43086         /**
43087          * @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
43088          */
43089         preferedCountries: false,
43090         
43091         getAutoCreate : function()
43092         {
43093             var data = Roo.bootstrap.PhoneInputData();
43094             var align = this.labelAlign || this.parentLabelAlign();
43095             var id = Roo.id();
43096             
43097             this.allCountries = [];
43098             this.dialCodeMapping = [];
43099             
43100             for (var i = 0; i < data.length; i++) {
43101               var c = data[i];
43102               this.allCountries[i] = {
43103                 name: c[0],
43104                 iso2: c[1],
43105                 dialCode: c[2],
43106                 priority: c[3] || 0,
43107                 areaCodes: c[4] || null
43108               };
43109               this.dialCodeMapping[c[2]] = {
43110                   name: c[0],
43111                   iso2: c[1],
43112                   priority: c[3] || 0,
43113                   areaCodes: c[4] || null
43114               };
43115             }
43116             
43117             var cfg = {
43118                 cls: 'form-group',
43119                 cn: []
43120             };
43121             
43122             var input =  {
43123                 tag: 'input',
43124                 id : id,
43125                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43126                 maxlength: this.max_length,
43127                 cls : 'form-control tel-input',
43128                 autocomplete: 'new-password'
43129             };
43130             
43131             var hiddenInput = {
43132                 tag: 'input',
43133                 type: 'hidden',
43134                 cls: 'hidden-tel-input'
43135             };
43136             
43137             if (this.name) {
43138                 hiddenInput.name = this.name;
43139             }
43140             
43141             if (this.disabled) {
43142                 input.disabled = true;
43143             }
43144             
43145             var flag_container = {
43146                 tag: 'div',
43147                 cls: 'flag-box',
43148                 cn: [
43149                     {
43150                         tag: 'div',
43151                         cls: 'flag'
43152                     },
43153                     {
43154                         tag: 'div',
43155                         cls: 'caret'
43156                     }
43157                 ]
43158             };
43159             
43160             var box = {
43161                 tag: 'div',
43162                 cls: this.hasFeedback ? 'has-feedback' : '',
43163                 cn: [
43164                     hiddenInput,
43165                     input,
43166                     {
43167                         tag: 'input',
43168                         cls: 'dial-code-holder',
43169                         disabled: true
43170                     }
43171                 ]
43172             };
43173             
43174             var container = {
43175                 cls: 'roo-select2-container input-group',
43176                 cn: [
43177                     flag_container,
43178                     box
43179                 ]
43180             };
43181             
43182             if (this.fieldLabel.length) {
43183                 var indicator = {
43184                     tag: 'i',
43185                     tooltip: 'This field is required'
43186                 };
43187                 
43188                 var label = {
43189                     tag: 'label',
43190                     'for':  id,
43191                     cls: 'control-label',
43192                     cn: []
43193                 };
43194                 
43195                 var label_text = {
43196                     tag: 'span',
43197                     html: this.fieldLabel
43198                 };
43199                 
43200                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43201                 label.cn = [
43202                     indicator,
43203                     label_text
43204                 ];
43205                 
43206                 if(this.indicatorpos == 'right') {
43207                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43208                     label.cn = [
43209                         label_text,
43210                         indicator
43211                     ];
43212                 }
43213                 
43214                 if(align == 'left') {
43215                     container = {
43216                         tag: 'div',
43217                         cn: [
43218                             container
43219                         ]
43220                     };
43221                     
43222                     if(this.labelWidth > 12){
43223                         label.style = "width: " + this.labelWidth + 'px';
43224                     }
43225                     if(this.labelWidth < 13 && this.labelmd == 0){
43226                         this.labelmd = this.labelWidth;
43227                     }
43228                     if(this.labellg > 0){
43229                         label.cls += ' col-lg-' + this.labellg;
43230                         input.cls += ' col-lg-' + (12 - this.labellg);
43231                     }
43232                     if(this.labelmd > 0){
43233                         label.cls += ' col-md-' + this.labelmd;
43234                         container.cls += ' col-md-' + (12 - this.labelmd);
43235                     }
43236                     if(this.labelsm > 0){
43237                         label.cls += ' col-sm-' + this.labelsm;
43238                         container.cls += ' col-sm-' + (12 - this.labelsm);
43239                     }
43240                     if(this.labelxs > 0){
43241                         label.cls += ' col-xs-' + this.labelxs;
43242                         container.cls += ' col-xs-' + (12 - this.labelxs);
43243                     }
43244                 }
43245             }
43246             
43247             cfg.cn = [
43248                 label,
43249                 container
43250             ];
43251             
43252             var settings = this;
43253             
43254             ['xs','sm','md','lg'].map(function(size){
43255                 if (settings[size]) {
43256                     cfg.cls += ' col-' + size + '-' + settings[size];
43257                 }
43258             });
43259             
43260             this.store = new Roo.data.Store({
43261                 proxy : new Roo.data.MemoryProxy({}),
43262                 reader : new Roo.data.JsonReader({
43263                     fields : [
43264                         {
43265                             'name' : 'name',
43266                             'type' : 'string'
43267                         },
43268                         {
43269                             'name' : 'iso2',
43270                             'type' : 'string'
43271                         },
43272                         {
43273                             'name' : 'dialCode',
43274                             'type' : 'string'
43275                         },
43276                         {
43277                             'name' : 'priority',
43278                             'type' : 'string'
43279                         },
43280                         {
43281                             'name' : 'areaCodes',
43282                             'type' : 'string'
43283                         }
43284                     ]
43285                 })
43286             });
43287             
43288             if(!this.preferedCountries) {
43289                 this.preferedCountries = [
43290                     'hk',
43291                     'gb',
43292                     'us'
43293                 ];
43294             }
43295             
43296             var p = this.preferedCountries.reverse();
43297             
43298             if(p) {
43299                 for (var i = 0; i < p.length; i++) {
43300                     for (var j = 0; j < this.allCountries.length; j++) {
43301                         if(this.allCountries[j].iso2 == p[i]) {
43302                             var t = this.allCountries[j];
43303                             this.allCountries.splice(j,1);
43304                             this.allCountries.unshift(t);
43305                         }
43306                     } 
43307                 }
43308             }
43309             
43310             this.store.proxy.data = {
43311                 success: true,
43312                 data: this.allCountries
43313             };
43314             
43315             return cfg;
43316         },
43317         
43318         initEvents : function()
43319         {
43320             this.createList();
43321             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43322             
43323             this.indicator = this.indicatorEl();
43324             this.flag = this.flagEl();
43325             this.dialCodeHolder = this.dialCodeHolderEl();
43326             
43327             this.trigger = this.el.select('div.flag-box',true).first();
43328             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43329             
43330             var _this = this;
43331             
43332             (function(){
43333                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43334                 _this.list.setWidth(lw);
43335             }).defer(100);
43336             
43337             this.list.on('mouseover', this.onViewOver, this);
43338             this.list.on('mousemove', this.onViewMove, this);
43339             this.inputEl().on("keyup", this.onKeyUp, this);
43340             this.inputEl().on("keypress", this.onKeyPress, this);
43341             
43342             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43343
43344             this.view = new Roo.View(this.list, this.tpl, {
43345                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43346             });
43347             
43348             this.view.on('click', this.onViewClick, this);
43349             this.setValue(this.defaultDialCode);
43350         },
43351         
43352         onTriggerClick : function(e)
43353         {
43354             Roo.log('trigger click');
43355             if(this.disabled){
43356                 return;
43357             }
43358             
43359             if(this.isExpanded()){
43360                 this.collapse();
43361                 this.hasFocus = false;
43362             }else {
43363                 this.store.load({});
43364                 this.hasFocus = true;
43365                 this.expand();
43366             }
43367         },
43368         
43369         isExpanded : function()
43370         {
43371             return this.list.isVisible();
43372         },
43373         
43374         collapse : function()
43375         {
43376             if(!this.isExpanded()){
43377                 return;
43378             }
43379             this.list.hide();
43380             Roo.get(document).un('mousedown', this.collapseIf, this);
43381             Roo.get(document).un('mousewheel', this.collapseIf, this);
43382             this.fireEvent('collapse', this);
43383             this.validate();
43384         },
43385         
43386         expand : function()
43387         {
43388             Roo.log('expand');
43389
43390             if(this.isExpanded() || !this.hasFocus){
43391                 return;
43392             }
43393             
43394             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43395             this.list.setWidth(lw);
43396             
43397             this.list.show();
43398             this.restrictHeight();
43399             
43400             Roo.get(document).on('mousedown', this.collapseIf, this);
43401             Roo.get(document).on('mousewheel', this.collapseIf, this);
43402             
43403             this.fireEvent('expand', this);
43404         },
43405         
43406         restrictHeight : function()
43407         {
43408             this.list.alignTo(this.inputEl(), this.listAlign);
43409             this.list.alignTo(this.inputEl(), this.listAlign);
43410         },
43411         
43412         onViewOver : function(e, t)
43413         {
43414             if(this.inKeyMode){
43415                 return;
43416             }
43417             var item = this.view.findItemFromChild(t);
43418             
43419             if(item){
43420                 var index = this.view.indexOf(item);
43421                 this.select(index, false);
43422             }
43423         },
43424
43425         // private
43426         onViewClick : function(view, doFocus, el, e)
43427         {
43428             var index = this.view.getSelectedIndexes()[0];
43429             
43430             var r = this.store.getAt(index);
43431             
43432             if(r){
43433                 this.onSelect(r, index);
43434             }
43435             if(doFocus !== false && !this.blockFocus){
43436                 this.inputEl().focus();
43437             }
43438         },
43439         
43440         onViewMove : function(e, t)
43441         {
43442             this.inKeyMode = false;
43443         },
43444         
43445         select : function(index, scrollIntoView)
43446         {
43447             this.selectedIndex = index;
43448             this.view.select(index);
43449             if(scrollIntoView !== false){
43450                 var el = this.view.getNode(index);
43451                 if(el){
43452                     this.list.scrollChildIntoView(el, false);
43453                 }
43454             }
43455         },
43456         
43457         createList : function()
43458         {
43459             this.list = Roo.get(document.body).createChild({
43460                 tag: 'ul',
43461                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43462                 style: 'display:none'
43463             });
43464             
43465             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43466         },
43467         
43468         collapseIf : function(e)
43469         {
43470             var in_combo  = e.within(this.el);
43471             var in_list =  e.within(this.list);
43472             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43473             
43474             if (in_combo || in_list || is_list) {
43475                 return;
43476             }
43477             this.collapse();
43478         },
43479         
43480         onSelect : function(record, index)
43481         {
43482             if(this.fireEvent('beforeselect', this, record, index) !== false){
43483                 
43484                 this.setFlagClass(record.data.iso2);
43485                 this.setDialCode(record.data.dialCode);
43486                 this.hasFocus = false;
43487                 this.collapse();
43488                 this.fireEvent('select', this, record, index);
43489             }
43490         },
43491         
43492         flagEl : function()
43493         {
43494             var flag = this.el.select('div.flag',true).first();
43495             if(!flag){
43496                 return false;
43497             }
43498             return flag;
43499         },
43500         
43501         dialCodeHolderEl : function()
43502         {
43503             var d = this.el.select('input.dial-code-holder',true).first();
43504             if(!d){
43505                 return false;
43506             }
43507             return d;
43508         },
43509         
43510         setDialCode : function(v)
43511         {
43512             this.dialCodeHolder.dom.value = '+'+v;
43513         },
43514         
43515         setFlagClass : function(n)
43516         {
43517             this.flag.dom.className = 'flag '+n;
43518         },
43519         
43520         getValue : function()
43521         {
43522             var v = this.inputEl().getValue();
43523             if(this.dialCodeHolder) {
43524                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43525             }
43526             return v;
43527         },
43528         
43529         setValue : function(v)
43530         {
43531             var d = this.getDialCode(v);
43532             
43533             //invalid dial code
43534             if(v.length == 0 || !d || d.length == 0) {
43535                 if(this.rendered){
43536                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43537                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43538                 }
43539                 return;
43540             }
43541             
43542             //valid dial code
43543             this.setFlagClass(this.dialCodeMapping[d].iso2);
43544             this.setDialCode(d);
43545             this.inputEl().dom.value = v.replace('+'+d,'');
43546             this.hiddenEl().dom.value = this.getValue();
43547             
43548             this.validate();
43549         },
43550         
43551         getDialCode : function(v)
43552         {
43553             v = v ||  '';
43554             
43555             if (v.length == 0) {
43556                 return this.dialCodeHolder.dom.value;
43557             }
43558             
43559             var dialCode = "";
43560             if (v.charAt(0) != "+") {
43561                 return false;
43562             }
43563             var numericChars = "";
43564             for (var i = 1; i < v.length; i++) {
43565               var c = v.charAt(i);
43566               if (!isNaN(c)) {
43567                 numericChars += c;
43568                 if (this.dialCodeMapping[numericChars]) {
43569                   dialCode = v.substr(1, i);
43570                 }
43571                 if (numericChars.length == 4) {
43572                   break;
43573                 }
43574               }
43575             }
43576             return dialCode;
43577         },
43578         
43579         reset : function()
43580         {
43581             this.setValue(this.defaultDialCode);
43582             this.validate();
43583         },
43584         
43585         hiddenEl : function()
43586         {
43587             return this.el.select('input.hidden-tel-input',true).first();
43588         },
43589         
43590         // after setting val
43591         onKeyUp : function(e){
43592             this.setValue(this.getValue());
43593         },
43594         
43595         onKeyPress : function(e){
43596             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43597                 e.stopEvent();
43598             }
43599         }
43600         
43601 });
43602 /**
43603  * @class Roo.bootstrap.MoneyField
43604  * @extends Roo.bootstrap.ComboBox
43605  * Bootstrap MoneyField class
43606  * 
43607  * @constructor
43608  * Create a new MoneyField.
43609  * @param {Object} config Configuration options
43610  */
43611
43612 Roo.bootstrap.MoneyField = function(config) {
43613     
43614     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43615     
43616 };
43617
43618 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43619     
43620     /**
43621      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43622      */
43623     allowDecimals : true,
43624     /**
43625      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43626      */
43627     decimalSeparator : ".",
43628     /**
43629      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43630      */
43631     decimalPrecision : 0,
43632     /**
43633      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43634      */
43635     allowNegative : true,
43636     /**
43637      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43638      */
43639     allowZero: true,
43640     /**
43641      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43642      */
43643     minValue : Number.NEGATIVE_INFINITY,
43644     /**
43645      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43646      */
43647     maxValue : Number.MAX_VALUE,
43648     /**
43649      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43650      */
43651     minText : "The minimum value for this field is {0}",
43652     /**
43653      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43654      */
43655     maxText : "The maximum value for this field is {0}",
43656     /**
43657      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43658      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43659      */
43660     nanText : "{0} is not a valid number",
43661     /**
43662      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43663      */
43664     castInt : true,
43665     /**
43666      * @cfg {String} defaults currency of the MoneyField
43667      * value should be in lkey
43668      */
43669     defaultCurrency : false,
43670     /**
43671      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43672      */
43673     thousandsDelimiter : false,
43674     /**
43675      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43676      */
43677     max_length: false,
43678     
43679     inputlg : 9,
43680     inputmd : 9,
43681     inputsm : 9,
43682     inputxs : 6,
43683     
43684     store : false,
43685     
43686     getAutoCreate : function()
43687     {
43688         var align = this.labelAlign || this.parentLabelAlign();
43689         
43690         var id = Roo.id();
43691
43692         var cfg = {
43693             cls: 'form-group',
43694             cn: []
43695         };
43696
43697         var input =  {
43698             tag: 'input',
43699             id : id,
43700             cls : 'form-control roo-money-amount-input',
43701             autocomplete: 'new-password'
43702         };
43703         
43704         var hiddenInput = {
43705             tag: 'input',
43706             type: 'hidden',
43707             id: Roo.id(),
43708             cls: 'hidden-number-input'
43709         };
43710         
43711         if(this.max_length) {
43712             input.maxlength = this.max_length; 
43713         }
43714         
43715         if (this.name) {
43716             hiddenInput.name = this.name;
43717         }
43718
43719         if (this.disabled) {
43720             input.disabled = true;
43721         }
43722
43723         var clg = 12 - this.inputlg;
43724         var cmd = 12 - this.inputmd;
43725         var csm = 12 - this.inputsm;
43726         var cxs = 12 - this.inputxs;
43727         
43728         var container = {
43729             tag : 'div',
43730             cls : 'row roo-money-field',
43731             cn : [
43732                 {
43733                     tag : 'div',
43734                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43735                     cn : [
43736                         {
43737                             tag : 'div',
43738                             cls: 'roo-select2-container input-group',
43739                             cn: [
43740                                 {
43741                                     tag : 'input',
43742                                     cls : 'form-control roo-money-currency-input',
43743                                     autocomplete: 'new-password',
43744                                     readOnly : 1,
43745                                     name : this.currencyName
43746                                 },
43747                                 {
43748                                     tag :'span',
43749                                     cls : 'input-group-addon',
43750                                     cn : [
43751                                         {
43752                                             tag: 'span',
43753                                             cls: 'caret'
43754                                         }
43755                                     ]
43756                                 }
43757                             ]
43758                         }
43759                     ]
43760                 },
43761                 {
43762                     tag : 'div',
43763                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43764                     cn : [
43765                         {
43766                             tag: 'div',
43767                             cls: this.hasFeedback ? 'has-feedback' : '',
43768                             cn: [
43769                                 input
43770                             ]
43771                         }
43772                     ]
43773                 }
43774             ]
43775             
43776         };
43777         
43778         if (this.fieldLabel.length) {
43779             var indicator = {
43780                 tag: 'i',
43781                 tooltip: 'This field is required'
43782             };
43783
43784             var label = {
43785                 tag: 'label',
43786                 'for':  id,
43787                 cls: 'control-label',
43788                 cn: []
43789             };
43790
43791             var label_text = {
43792                 tag: 'span',
43793                 html: this.fieldLabel
43794             };
43795
43796             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43797             label.cn = [
43798                 indicator,
43799                 label_text
43800             ];
43801
43802             if(this.indicatorpos == 'right') {
43803                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43804                 label.cn = [
43805                     label_text,
43806                     indicator
43807                 ];
43808             }
43809
43810             if(align == 'left') {
43811                 container = {
43812                     tag: 'div',
43813                     cn: [
43814                         container
43815                     ]
43816                 };
43817
43818                 if(this.labelWidth > 12){
43819                     label.style = "width: " + this.labelWidth + 'px';
43820                 }
43821                 if(this.labelWidth < 13 && this.labelmd == 0){
43822                     this.labelmd = this.labelWidth;
43823                 }
43824                 if(this.labellg > 0){
43825                     label.cls += ' col-lg-' + this.labellg;
43826                     input.cls += ' col-lg-' + (12 - this.labellg);
43827                 }
43828                 if(this.labelmd > 0){
43829                     label.cls += ' col-md-' + this.labelmd;
43830                     container.cls += ' col-md-' + (12 - this.labelmd);
43831                 }
43832                 if(this.labelsm > 0){
43833                     label.cls += ' col-sm-' + this.labelsm;
43834                     container.cls += ' col-sm-' + (12 - this.labelsm);
43835                 }
43836                 if(this.labelxs > 0){
43837                     label.cls += ' col-xs-' + this.labelxs;
43838                     container.cls += ' col-xs-' + (12 - this.labelxs);
43839                 }
43840             }
43841         }
43842
43843         cfg.cn = [
43844             label,
43845             container,
43846             hiddenInput
43847         ];
43848         
43849         var settings = this;
43850
43851         ['xs','sm','md','lg'].map(function(size){
43852             if (settings[size]) {
43853                 cfg.cls += ' col-' + size + '-' + settings[size];
43854             }
43855         });
43856         
43857         return cfg;
43858     },
43859     
43860     initEvents : function()
43861     {
43862         this.indicator = this.indicatorEl();
43863         
43864         this.initCurrencyEvent();
43865         
43866         this.initNumberEvent();
43867     },
43868     
43869     initCurrencyEvent : function()
43870     {
43871         if (!this.store) {
43872             throw "can not find store for combo";
43873         }
43874         
43875         this.store = Roo.factory(this.store, Roo.data);
43876         this.store.parent = this;
43877         
43878         this.createList();
43879         
43880         this.triggerEl = this.el.select('.input-group-addon', true).first();
43881         
43882         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43883         
43884         var _this = this;
43885         
43886         (function(){
43887             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43888             _this.list.setWidth(lw);
43889         }).defer(100);
43890         
43891         this.list.on('mouseover', this.onViewOver, this);
43892         this.list.on('mousemove', this.onViewMove, this);
43893         this.list.on('scroll', this.onViewScroll, this);
43894         
43895         if(!this.tpl){
43896             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43897         }
43898         
43899         this.view = new Roo.View(this.list, this.tpl, {
43900             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43901         });
43902         
43903         this.view.on('click', this.onViewClick, this);
43904         
43905         this.store.on('beforeload', this.onBeforeLoad, this);
43906         this.store.on('load', this.onLoad, this);
43907         this.store.on('loadexception', this.onLoadException, this);
43908         
43909         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43910             "up" : function(e){
43911                 this.inKeyMode = true;
43912                 this.selectPrev();
43913             },
43914
43915             "down" : function(e){
43916                 if(!this.isExpanded()){
43917                     this.onTriggerClick();
43918                 }else{
43919                     this.inKeyMode = true;
43920                     this.selectNext();
43921                 }
43922             },
43923
43924             "enter" : function(e){
43925                 this.collapse();
43926                 
43927                 if(this.fireEvent("specialkey", this, e)){
43928                     this.onViewClick(false);
43929                 }
43930                 
43931                 return true;
43932             },
43933
43934             "esc" : function(e){
43935                 this.collapse();
43936             },
43937
43938             "tab" : function(e){
43939                 this.collapse();
43940                 
43941                 if(this.fireEvent("specialkey", this, e)){
43942                     this.onViewClick(false);
43943                 }
43944                 
43945                 return true;
43946             },
43947
43948             scope : this,
43949
43950             doRelay : function(foo, bar, hname){
43951                 if(hname == 'down' || this.scope.isExpanded()){
43952                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43953                 }
43954                 return true;
43955             },
43956
43957             forceKeyDown: true
43958         });
43959         
43960         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43961         
43962     },
43963     
43964     initNumberEvent : function(e)
43965     {
43966         this.inputEl().on("keydown" , this.fireKey,  this);
43967         this.inputEl().on("focus", this.onFocus,  this);
43968         this.inputEl().on("blur", this.onBlur,  this);
43969         
43970         this.inputEl().relayEvent('keyup', this);
43971         
43972         if(this.indicator){
43973             this.indicator.addClass('invisible');
43974         }
43975  
43976         this.originalValue = this.getValue();
43977         
43978         if(this.validationEvent == 'keyup'){
43979             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43980             this.inputEl().on('keyup', this.filterValidation, this);
43981         }
43982         else if(this.validationEvent !== false){
43983             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43984         }
43985         
43986         if(this.selectOnFocus){
43987             this.on("focus", this.preFocus, this);
43988             
43989         }
43990         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43991             this.inputEl().on("keypress", this.filterKeys, this);
43992         } else {
43993             this.inputEl().relayEvent('keypress', this);
43994         }
43995         
43996         var allowed = "0123456789";
43997         
43998         if(this.allowDecimals){
43999             allowed += this.decimalSeparator;
44000         }
44001         
44002         if(this.allowNegative){
44003             allowed += "-";
44004         }
44005         
44006         if(this.thousandsDelimiter) {
44007             allowed += ",";
44008         }
44009         
44010         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44011         
44012         var keyPress = function(e){
44013             
44014             var k = e.getKey();
44015             
44016             var c = e.getCharCode();
44017             
44018             if(
44019                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44020                     allowed.indexOf(String.fromCharCode(c)) === -1
44021             ){
44022                 e.stopEvent();
44023                 return;
44024             }
44025             
44026             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44027                 return;
44028             }
44029             
44030             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44031                 e.stopEvent();
44032             }
44033         };
44034         
44035         this.inputEl().on("keypress", keyPress, this);
44036         
44037     },
44038     
44039     onTriggerClick : function(e)
44040     {   
44041         if(this.disabled){
44042             return;
44043         }
44044         
44045         this.page = 0;
44046         this.loadNext = false;
44047         
44048         if(this.isExpanded()){
44049             this.collapse();
44050             return;
44051         }
44052         
44053         this.hasFocus = true;
44054         
44055         if(this.triggerAction == 'all') {
44056             this.doQuery(this.allQuery, true);
44057             return;
44058         }
44059         
44060         this.doQuery(this.getRawValue());
44061     },
44062     
44063     getCurrency : function()
44064     {   
44065         var v = this.currencyEl().getValue();
44066         
44067         return v;
44068     },
44069     
44070     restrictHeight : function()
44071     {
44072         this.list.alignTo(this.currencyEl(), this.listAlign);
44073         this.list.alignTo(this.currencyEl(), this.listAlign);
44074     },
44075     
44076     onViewClick : function(view, doFocus, el, e)
44077     {
44078         var index = this.view.getSelectedIndexes()[0];
44079         
44080         var r = this.store.getAt(index);
44081         
44082         if(r){
44083             this.onSelect(r, index);
44084         }
44085     },
44086     
44087     onSelect : function(record, index){
44088         
44089         if(this.fireEvent('beforeselect', this, record, index) !== false){
44090         
44091             this.setFromCurrencyData(index > -1 ? record.data : false);
44092             
44093             this.collapse();
44094             
44095             this.fireEvent('select', this, record, index);
44096         }
44097     },
44098     
44099     setFromCurrencyData : function(o)
44100     {
44101         var currency = '';
44102         
44103         this.lastCurrency = o;
44104         
44105         if (this.currencyField) {
44106             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44107         } else {
44108             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44109         }
44110         
44111         this.lastSelectionText = currency;
44112         
44113         //setting default currency
44114         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44115             this.setCurrency(this.defaultCurrency);
44116             return;
44117         }
44118         
44119         this.setCurrency(currency);
44120     },
44121     
44122     setFromData : function(o)
44123     {
44124         var c = {};
44125         
44126         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44127         
44128         this.setFromCurrencyData(c);
44129         
44130         var value = '';
44131         
44132         if (this.name) {
44133             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44134         } else {
44135             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44136         }
44137         
44138         this.setValue(value);
44139         
44140     },
44141     
44142     setCurrency : function(v)
44143     {   
44144         this.currencyValue = v;
44145         
44146         if(this.rendered){
44147             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44148             this.validate();
44149         }
44150     },
44151     
44152     setValue : function(v)
44153     {
44154         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44155         
44156         this.value = v;
44157         
44158         if(this.rendered){
44159             
44160             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44161             
44162             this.inputEl().dom.value = (v == '') ? '' :
44163                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44164             
44165             if(!this.allowZero && v === '0') {
44166                 this.hiddenEl().dom.value = '';
44167                 this.inputEl().dom.value = '';
44168             }
44169             
44170             this.validate();
44171         }
44172     },
44173     
44174     getRawValue : function()
44175     {
44176         var v = this.inputEl().getValue();
44177         
44178         return v;
44179     },
44180     
44181     getValue : function()
44182     {
44183         return this.fixPrecision(this.parseValue(this.getRawValue()));
44184     },
44185     
44186     parseValue : function(value)
44187     {
44188         if(this.thousandsDelimiter) {
44189             value += "";
44190             r = new RegExp(",", "g");
44191             value = value.replace(r, "");
44192         }
44193         
44194         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44195         return isNaN(value) ? '' : value;
44196         
44197     },
44198     
44199     fixPrecision : function(value)
44200     {
44201         if(this.thousandsDelimiter) {
44202             value += "";
44203             r = new RegExp(",", "g");
44204             value = value.replace(r, "");
44205         }
44206         
44207         var nan = isNaN(value);
44208         
44209         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44210             return nan ? '' : value;
44211         }
44212         return parseFloat(value).toFixed(this.decimalPrecision);
44213     },
44214     
44215     decimalPrecisionFcn : function(v)
44216     {
44217         return Math.floor(v);
44218     },
44219     
44220     validateValue : function(value)
44221     {
44222         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44223             return false;
44224         }
44225         
44226         var num = this.parseValue(value);
44227         
44228         if(isNaN(num)){
44229             this.markInvalid(String.format(this.nanText, value));
44230             return false;
44231         }
44232         
44233         if(num < this.minValue){
44234             this.markInvalid(String.format(this.minText, this.minValue));
44235             return false;
44236         }
44237         
44238         if(num > this.maxValue){
44239             this.markInvalid(String.format(this.maxText, this.maxValue));
44240             return false;
44241         }
44242         
44243         return true;
44244     },
44245     
44246     validate : function()
44247     {
44248         if(this.disabled || this.allowBlank){
44249             this.markValid();
44250             return true;
44251         }
44252         
44253         var currency = this.getCurrency();
44254         
44255         if(this.validateValue(this.getRawValue()) && currency.length){
44256             this.markValid();
44257             return true;
44258         }
44259         
44260         this.markInvalid();
44261         return false;
44262     },
44263     
44264     getName: function()
44265     {
44266         return this.name;
44267     },
44268     
44269     beforeBlur : function()
44270     {
44271         if(!this.castInt){
44272             return;
44273         }
44274         
44275         var v = this.parseValue(this.getRawValue());
44276         
44277         if(v || v == 0){
44278             this.setValue(v);
44279         }
44280     },
44281     
44282     onBlur : function()
44283     {
44284         this.beforeBlur();
44285         
44286         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44287             //this.el.removeClass(this.focusClass);
44288         }
44289         
44290         this.hasFocus = false;
44291         
44292         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44293             this.validate();
44294         }
44295         
44296         var v = this.getValue();
44297         
44298         if(String(v) !== String(this.startValue)){
44299             this.fireEvent('change', this, v, this.startValue);
44300         }
44301         
44302         this.fireEvent("blur", this);
44303     },
44304     
44305     inputEl : function()
44306     {
44307         return this.el.select('.roo-money-amount-input', true).first();
44308     },
44309     
44310     currencyEl : function()
44311     {
44312         return this.el.select('.roo-money-currency-input', true).first();
44313     },
44314     
44315     hiddenEl : function()
44316     {
44317         return this.el.select('input.hidden-number-input',true).first();
44318     }
44319     
44320 });/**
44321  * @class Roo.bootstrap.BezierSignature
44322  * @extends Roo.bootstrap.Component
44323  * Bootstrap BezierSignature class
44324  * This script refer to:
44325  *    Title: Signature Pad
44326  *    Author: szimek
44327  *    Availability: https://github.com/szimek/signature_pad
44328  *
44329  * @constructor
44330  * Create a new BezierSignature
44331  * @param {Object} config The config object
44332  */
44333
44334 Roo.bootstrap.BezierSignature = function(config){
44335     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44336     this.addEvents({
44337         "resize" : true
44338     });
44339 };
44340
44341 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44342 {
44343      
44344     curve_data: [],
44345     
44346     is_empty: true,
44347     
44348     mouse_btn_down: true,
44349     
44350     /**
44351      * @cfg {int} canvas height
44352      */
44353     canvas_height: '200px',
44354     
44355     /**
44356      * @cfg {float|function} Radius of a single dot.
44357      */ 
44358     dot_size: false,
44359     
44360     /**
44361      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44362      */
44363     min_width: 0.5,
44364     
44365     /**
44366      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44367      */
44368     max_width: 2.5,
44369     
44370     /**
44371      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44372      */
44373     throttle: 16,
44374     
44375     /**
44376      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44377      */
44378     min_distance: 5,
44379     
44380     /**
44381      * @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.
44382      */
44383     bg_color: 'rgba(0, 0, 0, 0)',
44384     
44385     /**
44386      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44387      */
44388     dot_color: 'black',
44389     
44390     /**
44391      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44392      */ 
44393     velocity_filter_weight: 0.7,
44394     
44395     /**
44396      * @cfg {function} Callback when stroke begin. 
44397      */
44398     onBegin: false,
44399     
44400     /**
44401      * @cfg {function} Callback when stroke end.
44402      */
44403     onEnd: false,
44404     
44405     getAutoCreate : function()
44406     {
44407         var cls = 'roo-signature column';
44408         
44409         if(this.cls){
44410             cls += ' ' + this.cls;
44411         }
44412         
44413         var col_sizes = [
44414             'lg',
44415             'md',
44416             'sm',
44417             'xs'
44418         ];
44419         
44420         for(var i = 0; i < col_sizes.length; i++) {
44421             if(this[col_sizes[i]]) {
44422                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44423             }
44424         }
44425         
44426         var cfg = {
44427             tag: 'div',
44428             cls: cls,
44429             cn: [
44430                 {
44431                     tag: 'div',
44432                     cls: 'roo-signature-body',
44433                     cn: [
44434                         {
44435                             tag: 'canvas',
44436                             cls: 'roo-signature-body-canvas',
44437                             height: this.canvas_height,
44438                             width: this.canvas_width
44439                         }
44440                     ]
44441                 },
44442                 {
44443                     tag: 'input',
44444                     type: 'file',
44445                     style: 'display: none'
44446                 }
44447             ]
44448         };
44449         
44450         return cfg;
44451     },
44452     
44453     initEvents: function() 
44454     {
44455         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44456         
44457         var canvas = this.canvasEl();
44458         
44459         // mouse && touch event swapping...
44460         canvas.dom.style.touchAction = 'none';
44461         canvas.dom.style.msTouchAction = 'none';
44462         
44463         this.mouse_btn_down = false;
44464         canvas.on('mousedown', this._handleMouseDown, this);
44465         canvas.on('mousemove', this._handleMouseMove, this);
44466         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44467         
44468         if (window.PointerEvent) {
44469             canvas.on('pointerdown', this._handleMouseDown, this);
44470             canvas.on('pointermove', this._handleMouseMove, this);
44471             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44472         }
44473         
44474         if ('ontouchstart' in window) {
44475             canvas.on('touchstart', this._handleTouchStart, this);
44476             canvas.on('touchmove', this._handleTouchMove, this);
44477             canvas.on('touchend', this._handleTouchEnd, this);
44478         }
44479         
44480         Roo.EventManager.onWindowResize(this.resize, this, true);
44481         
44482         // file input event
44483         this.fileEl().on('change', this.uploadImage, this);
44484         
44485         this.clear();
44486         
44487         this.resize();
44488     },
44489     
44490     resize: function(){
44491         
44492         var canvas = this.canvasEl().dom;
44493         var ctx = this.canvasElCtx();
44494         var img_data = false;
44495         
44496         if(canvas.width > 0) {
44497             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44498         }
44499         // setting canvas width will clean img data
44500         canvas.width = 0;
44501         
44502         var style = window.getComputedStyle ? 
44503             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44504             
44505         var padding_left = parseInt(style.paddingLeft) || 0;
44506         var padding_right = parseInt(style.paddingRight) || 0;
44507         
44508         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44509         
44510         if(img_data) {
44511             ctx.putImageData(img_data, 0, 0);
44512         }
44513     },
44514     
44515     _handleMouseDown: function(e)
44516     {
44517         if (e.browserEvent.which === 1) {
44518             this.mouse_btn_down = true;
44519             this.strokeBegin(e);
44520         }
44521     },
44522     
44523     _handleMouseMove: function (e)
44524     {
44525         if (this.mouse_btn_down) {
44526             this.strokeMoveUpdate(e);
44527         }
44528     },
44529     
44530     _handleMouseUp: function (e)
44531     {
44532         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44533             this.mouse_btn_down = false;
44534             this.strokeEnd(e);
44535         }
44536     },
44537     
44538     _handleTouchStart: function (e) {
44539         
44540         e.preventDefault();
44541         if (e.browserEvent.targetTouches.length === 1) {
44542             // var touch = e.browserEvent.changedTouches[0];
44543             // this.strokeBegin(touch);
44544             
44545              this.strokeBegin(e); // assume e catching the correct xy...
44546         }
44547     },
44548     
44549     _handleTouchMove: function (e) {
44550         e.preventDefault();
44551         // var touch = event.targetTouches[0];
44552         // _this._strokeMoveUpdate(touch);
44553         this.strokeMoveUpdate(e);
44554     },
44555     
44556     _handleTouchEnd: function (e) {
44557         var wasCanvasTouched = e.target === this.canvasEl().dom;
44558         if (wasCanvasTouched) {
44559             e.preventDefault();
44560             // var touch = event.changedTouches[0];
44561             // _this._strokeEnd(touch);
44562             this.strokeEnd(e);
44563         }
44564     },
44565     
44566     reset: function () {
44567         this._lastPoints = [];
44568         this._lastVelocity = 0;
44569         this._lastWidth = (this.min_width + this.max_width) / 2;
44570         this.canvasElCtx().fillStyle = this.dot_color;
44571     },
44572     
44573     strokeMoveUpdate: function(e)
44574     {
44575         this.strokeUpdate(e);
44576         
44577         if (this.throttle) {
44578             this.throttleStroke(this.strokeUpdate, this.throttle);
44579         }
44580         else {
44581             this.strokeUpdate(e);
44582         }
44583     },
44584     
44585     strokeBegin: function(e)
44586     {
44587         var newPointGroup = {
44588             color: this.dot_color,
44589             points: []
44590         };
44591         
44592         if (typeof this.onBegin === 'function') {
44593             this.onBegin(e);
44594         }
44595         
44596         this.curve_data.push(newPointGroup);
44597         this.reset();
44598         this.strokeUpdate(e);
44599     },
44600     
44601     strokeUpdate: function(e)
44602     {
44603         var rect = this.canvasEl().dom.getBoundingClientRect();
44604         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44605         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44606         var lastPoints = lastPointGroup.points;
44607         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44608         var isLastPointTooClose = lastPoint
44609             ? point.distanceTo(lastPoint) <= this.min_distance
44610             : false;
44611         var color = lastPointGroup.color;
44612         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44613             var curve = this.addPoint(point);
44614             if (!lastPoint) {
44615                 this.drawDot({color: color, point: point});
44616             }
44617             else if (curve) {
44618                 this.drawCurve({color: color, curve: curve});
44619             }
44620             lastPoints.push({
44621                 time: point.time,
44622                 x: point.x,
44623                 y: point.y
44624             });
44625         }
44626     },
44627     
44628     strokeEnd: function(e)
44629     {
44630         this.strokeUpdate(e);
44631         if (typeof this.onEnd === 'function') {
44632             this.onEnd(e);
44633         }
44634     },
44635     
44636     addPoint:  function (point) {
44637         var _lastPoints = this._lastPoints;
44638         _lastPoints.push(point);
44639         if (_lastPoints.length > 2) {
44640             if (_lastPoints.length === 3) {
44641                 _lastPoints.unshift(_lastPoints[0]);
44642             }
44643             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44644             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44645             _lastPoints.shift();
44646             return curve;
44647         }
44648         return null;
44649     },
44650     
44651     calculateCurveWidths: function (startPoint, endPoint) {
44652         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44653             (1 - this.velocity_filter_weight) * this._lastVelocity;
44654
44655         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44656         var widths = {
44657             end: newWidth,
44658             start: this._lastWidth
44659         };
44660         
44661         this._lastVelocity = velocity;
44662         this._lastWidth = newWidth;
44663         return widths;
44664     },
44665     
44666     drawDot: function (_a) {
44667         var color = _a.color, point = _a.point;
44668         var ctx = this.canvasElCtx();
44669         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44670         ctx.beginPath();
44671         this.drawCurveSegment(point.x, point.y, width);
44672         ctx.closePath();
44673         ctx.fillStyle = color;
44674         ctx.fill();
44675     },
44676     
44677     drawCurve: function (_a) {
44678         var color = _a.color, curve = _a.curve;
44679         var ctx = this.canvasElCtx();
44680         var widthDelta = curve.endWidth - curve.startWidth;
44681         var drawSteps = Math.floor(curve.length()) * 2;
44682         ctx.beginPath();
44683         ctx.fillStyle = color;
44684         for (var i = 0; i < drawSteps; i += 1) {
44685         var t = i / drawSteps;
44686         var tt = t * t;
44687         var ttt = tt * t;
44688         var u = 1 - t;
44689         var uu = u * u;
44690         var uuu = uu * u;
44691         var x = uuu * curve.startPoint.x;
44692         x += 3 * uu * t * curve.control1.x;
44693         x += 3 * u * tt * curve.control2.x;
44694         x += ttt * curve.endPoint.x;
44695         var y = uuu * curve.startPoint.y;
44696         y += 3 * uu * t * curve.control1.y;
44697         y += 3 * u * tt * curve.control2.y;
44698         y += ttt * curve.endPoint.y;
44699         var width = curve.startWidth + ttt * widthDelta;
44700         this.drawCurveSegment(x, y, width);
44701         }
44702         ctx.closePath();
44703         ctx.fill();
44704     },
44705     
44706     drawCurveSegment: function (x, y, width) {
44707         var ctx = this.canvasElCtx();
44708         ctx.moveTo(x, y);
44709         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44710         this.is_empty = false;
44711     },
44712     
44713     clear: function()
44714     {
44715         var ctx = this.canvasElCtx();
44716         var canvas = this.canvasEl().dom;
44717         ctx.fillStyle = this.bg_color;
44718         ctx.clearRect(0, 0, canvas.width, canvas.height);
44719         ctx.fillRect(0, 0, canvas.width, canvas.height);
44720         this.curve_data = [];
44721         this.reset();
44722         this.is_empty = true;
44723     },
44724     
44725     fileEl: function()
44726     {
44727         return  this.el.select('input',true).first();
44728     },
44729     
44730     canvasEl: function()
44731     {
44732         return this.el.select('canvas',true).first();
44733     },
44734     
44735     canvasElCtx: function()
44736     {
44737         return this.el.select('canvas',true).first().dom.getContext('2d');
44738     },
44739     
44740     getImage: function(type)
44741     {
44742         if(this.is_empty) {
44743             return false;
44744         }
44745         
44746         // encryption ?
44747         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44748     },
44749     
44750     drawFromImage: function(img_src)
44751     {
44752         var img = new Image();
44753         
44754         img.onload = function(){
44755             this.canvasElCtx().drawImage(img, 0, 0);
44756         }.bind(this);
44757         
44758         img.src = img_src;
44759         
44760         this.is_empty = false;
44761     },
44762     
44763     selectImage: function()
44764     {
44765         this.fileEl().dom.click();
44766     },
44767     
44768     uploadImage: function(e)
44769     {
44770         var reader = new FileReader();
44771         
44772         reader.onload = function(e){
44773             var img = new Image();
44774             img.onload = function(){
44775                 this.reset();
44776                 this.canvasElCtx().drawImage(img, 0, 0);
44777             }.bind(this);
44778             img.src = e.target.result;
44779         }.bind(this);
44780         
44781         reader.readAsDataURL(e.target.files[0]);
44782     },
44783     
44784     // Bezier Point Constructor
44785     Point: (function () {
44786         function Point(x, y, time) {
44787             this.x = x;
44788             this.y = y;
44789             this.time = time || Date.now();
44790         }
44791         Point.prototype.distanceTo = function (start) {
44792             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44793         };
44794         Point.prototype.equals = function (other) {
44795             return this.x === other.x && this.y === other.y && this.time === other.time;
44796         };
44797         Point.prototype.velocityFrom = function (start) {
44798             return this.time !== start.time
44799             ? this.distanceTo(start) / (this.time - start.time)
44800             : 0;
44801         };
44802         return Point;
44803     }()),
44804     
44805     
44806     // Bezier Constructor
44807     Bezier: (function () {
44808         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44809             this.startPoint = startPoint;
44810             this.control2 = control2;
44811             this.control1 = control1;
44812             this.endPoint = endPoint;
44813             this.startWidth = startWidth;
44814             this.endWidth = endWidth;
44815         }
44816         Bezier.fromPoints = function (points, widths, scope) {
44817             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44818             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44819             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44820         };
44821         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44822             var dx1 = s1.x - s2.x;
44823             var dy1 = s1.y - s2.y;
44824             var dx2 = s2.x - s3.x;
44825             var dy2 = s2.y - s3.y;
44826             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44827             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44828             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44829             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44830             var dxm = m1.x - m2.x;
44831             var dym = m1.y - m2.y;
44832             var k = l2 / (l1 + l2);
44833             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44834             var tx = s2.x - cm.x;
44835             var ty = s2.y - cm.y;
44836             return {
44837                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44838                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44839             };
44840         };
44841         Bezier.prototype.length = function () {
44842             var steps = 10;
44843             var length = 0;
44844             var px;
44845             var py;
44846             for (var i = 0; i <= steps; i += 1) {
44847                 var t = i / steps;
44848                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44849                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44850                 if (i > 0) {
44851                     var xdiff = cx - px;
44852                     var ydiff = cy - py;
44853                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44854                 }
44855                 px = cx;
44856                 py = cy;
44857             }
44858             return length;
44859         };
44860         Bezier.prototype.point = function (t, start, c1, c2, end) {
44861             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44862             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44863             + (3.0 * c2 * (1.0 - t) * t * t)
44864             + (end * t * t * t);
44865         };
44866         return Bezier;
44867     }()),
44868     
44869     throttleStroke: function(fn, wait) {
44870       if (wait === void 0) { wait = 250; }
44871       var previous = 0;
44872       var timeout = null;
44873       var result;
44874       var storedContext;
44875       var storedArgs;
44876       var later = function () {
44877           previous = Date.now();
44878           timeout = null;
44879           result = fn.apply(storedContext, storedArgs);
44880           if (!timeout) {
44881               storedContext = null;
44882               storedArgs = [];
44883           }
44884       };
44885       return function wrapper() {
44886           var args = [];
44887           for (var _i = 0; _i < arguments.length; _i++) {
44888               args[_i] = arguments[_i];
44889           }
44890           var now = Date.now();
44891           var remaining = wait - (now - previous);
44892           storedContext = this;
44893           storedArgs = args;
44894           if (remaining <= 0 || remaining > wait) {
44895               if (timeout) {
44896                   clearTimeout(timeout);
44897                   timeout = null;
44898               }
44899               previous = now;
44900               result = fn.apply(storedContext, storedArgs);
44901               if (!timeout) {
44902                   storedContext = null;
44903                   storedArgs = [];
44904               }
44905           }
44906           else if (!timeout) {
44907               timeout = window.setTimeout(later, remaining);
44908           }
44909           return result;
44910       };
44911   }
44912   
44913 });
44914
44915  
44916
44917