8fa18be727f9c9a9548cefa92bf12b406e6875d3
[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          * @event load
3065          * The when any image loads
3066          * @param {Roo.EventObject} e
3067          */
3068         "load" : true
3069     });
3070 };
3071
3072 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3073     
3074     imgResponsive: true,
3075     border: '',
3076     src: 'about:blank',
3077     href: false,
3078     target: false,
3079     xsUrl: '',
3080     smUrl: '',
3081     mdUrl: '',
3082     lgUrl: '',
3083
3084     getAutoCreate : function()
3085     {   
3086         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3087             return this.createSingleImg();
3088         }
3089         
3090         var cfg = {
3091             tag: 'div',
3092             cls: 'roo-image-responsive-group',
3093             cn: []
3094         };
3095         var _this = this;
3096         
3097         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3098             
3099             if(!_this[size + 'Url']){
3100                 return;
3101             }
3102             
3103             var img = {
3104                 tag: 'img',
3105                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3106                 html: _this.html || cfg.html,
3107                 src: _this[size + 'Url']
3108             };
3109             
3110             img.cls += ' roo-image-responsive-' + size;
3111             
3112             var s = ['xs', 'sm', 'md', 'lg'];
3113             
3114             s.splice(s.indexOf(size), 1);
3115             
3116             Roo.each(s, function(ss){
3117                 img.cls += ' hidden-' + ss;
3118             });
3119             
3120             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3121                 cfg.cls += ' img-' + _this.border;
3122             }
3123             
3124             if(_this.alt){
3125                 cfg.alt = _this.alt;
3126             }
3127             
3128             if(_this.href){
3129                 var a = {
3130                     tag: 'a',
3131                     href: _this.href,
3132                     cn: [
3133                         img
3134                     ]
3135                 };
3136
3137                 if(this.target){
3138                     a.target = _this.target;
3139                 }
3140             }
3141             
3142             cfg.cn.push((_this.href) ? a : img);
3143             
3144         });
3145         
3146         return cfg;
3147     },
3148     
3149     createSingleImg : function()
3150     {
3151         var cfg = {
3152             tag: 'img',
3153             cls: (this.imgResponsive) ? 'img-responsive' : '',
3154             html : null,
3155             src : 'about:blank'  // just incase src get's set to undefined?!?
3156         };
3157         
3158         cfg.html = this.html || cfg.html;
3159         
3160         cfg.src = this.src || cfg.src;
3161         
3162         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3163             cfg.cls += ' img-' + this.border;
3164         }
3165         
3166         if(this.alt){
3167             cfg.alt = this.alt;
3168         }
3169         
3170         if(this.href){
3171             var a = {
3172                 tag: 'a',
3173                 href: this.href,
3174                 cn: [
3175                     cfg
3176                 ]
3177             };
3178             
3179             if(this.target){
3180                 a.target = this.target;
3181             }
3182             
3183         }
3184         
3185         return (this.href) ? a : cfg;
3186     },
3187     
3188     initEvents: function() 
3189     {
3190         if(!this.href){
3191             this.el.on('click', this.onClick, this);
3192         }
3193         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3194             this.el.on('loac', this.onImageLoad, this);
3195             
3196         } else {
3197             // not sure if this works.. not tested
3198             this.el.select('img', true).on('load', this.onImageLoad, this);
3199         }
3200         
3201     },
3202     
3203     onClick : function(e)
3204     {
3205         Roo.log('img onclick');
3206         this.fireEvent('click', this, e);
3207     },
3208     onImageLoad: function(e)
3209     {
3210         Roo.log('img load');
3211         this.fireEvent('load', this, e);
3212     },
3213     
3214     /**
3215      * Sets the url of the image - used to update it
3216      * @param {String} url the url of the image
3217      */
3218     
3219     setSrc : function(url)
3220     {
3221         this.src =  url;
3222         
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.dom.src =  url;
3225             return;
3226         }
3227         
3228         this.el.select('img', true).first().dom.src =  url;
3229     }
3230     
3231     
3232    
3233 });
3234
3235  /*
3236  * - LGPL
3237  *
3238  * image
3239  * 
3240  */
3241
3242
3243 /**
3244  * @class Roo.bootstrap.Link
3245  * @extends Roo.bootstrap.Component
3246  * Bootstrap Link Class
3247  * @cfg {String} alt image alternative text
3248  * @cfg {String} href a tag href
3249  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3250  * @cfg {String} html the content of the link.
3251  * @cfg {String} anchor name for the anchor link
3252  * @cfg {String} fa - favicon
3253
3254  * @cfg {Boolean} preventDefault (true | false) default false
3255
3256  * 
3257  * @constructor
3258  * Create a new Input
3259  * @param {Object} config The config object
3260  */
3261
3262 Roo.bootstrap.Link = function(config){
3263     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3264     
3265     this.addEvents({
3266         // img events
3267         /**
3268          * @event click
3269          * The img click event for the img.
3270          * @param {Roo.EventObject} e
3271          */
3272         "click" : true
3273     });
3274 };
3275
3276 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3277     
3278     href: false,
3279     target: false,
3280     preventDefault: false,
3281     anchor : false,
3282     alt : false,
3283     fa: false,
3284
3285
3286     getAutoCreate : function()
3287     {
3288         var html = this.html || '';
3289         
3290         if (this.fa !== false) {
3291             html = '<i class="fa fa-' + this.fa + '"></i>';
3292         }
3293         var cfg = {
3294             tag: 'a'
3295         };
3296         // anchor's do not require html/href...
3297         if (this.anchor === false) {
3298             cfg.html = html;
3299             cfg.href = this.href || '#';
3300         } else {
3301             cfg.name = this.anchor;
3302             if (this.html !== false || this.fa !== false) {
3303                 cfg.html = html;
3304             }
3305             if (this.href !== false) {
3306                 cfg.href = this.href;
3307             }
3308         }
3309         
3310         if(this.alt !== false){
3311             cfg.alt = this.alt;
3312         }
3313         
3314         
3315         if(this.target !== false) {
3316             cfg.target = this.target;
3317         }
3318         
3319         return cfg;
3320     },
3321     
3322     initEvents: function() {
3323         
3324         if(!this.href || this.preventDefault){
3325             this.el.on('click', this.onClick, this);
3326         }
3327     },
3328     
3329     onClick : function(e)
3330     {
3331         if(this.preventDefault){
3332             e.preventDefault();
3333         }
3334         //Roo.log('img onclick');
3335         this.fireEvent('click', this, e);
3336     }
3337    
3338 });
3339
3340  /*
3341  * - LGPL
3342  *
3343  * header
3344  * 
3345  */
3346
3347 /**
3348  * @class Roo.bootstrap.Header
3349  * @extends Roo.bootstrap.Component
3350  * Bootstrap Header class
3351  * @cfg {String} html content of header
3352  * @cfg {Number} level (1|2|3|4|5|6) default 1
3353  * 
3354  * @constructor
3355  * Create a new Header
3356  * @param {Object} config The config object
3357  */
3358
3359
3360 Roo.bootstrap.Header  = function(config){
3361     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3362 };
3363
3364 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3365     
3366     //href : false,
3367     html : false,
3368     level : 1,
3369     
3370     
3371     
3372     getAutoCreate : function(){
3373         
3374         
3375         
3376         var cfg = {
3377             tag: 'h' + (1 *this.level),
3378             html: this.html || ''
3379         } ;
3380         
3381         return cfg;
3382     }
3383    
3384 });
3385
3386  
3387
3388  /*
3389  * Based on:
3390  * Ext JS Library 1.1.1
3391  * Copyright(c) 2006-2007, Ext JS, LLC.
3392  *
3393  * Originally Released Under LGPL - original licence link has changed is not relivant.
3394  *
3395  * Fork - LGPL
3396  * <script type="text/javascript">
3397  */
3398  
3399 /**
3400  * @class Roo.bootstrap.MenuMgr
3401  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3402  * @singleton
3403  */
3404 Roo.bootstrap.MenuMgr = function(){
3405    var menus, active, groups = {}, attached = false, lastShow = new Date();
3406
3407    // private - called when first menu is created
3408    function init(){
3409        menus = {};
3410        active = new Roo.util.MixedCollection();
3411        Roo.get(document).addKeyListener(27, function(){
3412            if(active.length > 0){
3413                hideAll();
3414            }
3415        });
3416    }
3417
3418    // private
3419    function hideAll(){
3420        if(active && active.length > 0){
3421            var c = active.clone();
3422            c.each(function(m){
3423                m.hide();
3424            });
3425        }
3426    }
3427
3428    // private
3429    function onHide(m){
3430        active.remove(m);
3431        if(active.length < 1){
3432            Roo.get(document).un("mouseup", onMouseDown);
3433             
3434            attached = false;
3435        }
3436    }
3437
3438    // private
3439    function onShow(m){
3440        var last = active.last();
3441        lastShow = new Date();
3442        active.add(m);
3443        if(!attached){
3444           Roo.get(document).on("mouseup", onMouseDown);
3445            
3446            attached = true;
3447        }
3448        if(m.parentMenu){
3449           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3450           m.parentMenu.activeChild = m;
3451        }else if(last && last.isVisible()){
3452           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3453        }
3454    }
3455
3456    // private
3457    function onBeforeHide(m){
3458        if(m.activeChild){
3459            m.activeChild.hide();
3460        }
3461        if(m.autoHideTimer){
3462            clearTimeout(m.autoHideTimer);
3463            delete m.autoHideTimer;
3464        }
3465    }
3466
3467    // private
3468    function onBeforeShow(m){
3469        var pm = m.parentMenu;
3470        if(!pm && !m.allowOtherMenus){
3471            hideAll();
3472        }else if(pm && pm.activeChild && active != m){
3473            pm.activeChild.hide();
3474        }
3475    }
3476
3477    // private this should really trigger on mouseup..
3478    function onMouseDown(e){
3479         Roo.log("on Mouse Up");
3480         
3481         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3482             Roo.log("MenuManager hideAll");
3483             hideAll();
3484             e.stopEvent();
3485         }
3486         
3487         
3488    }
3489
3490    // private
3491    function onBeforeCheck(mi, state){
3492        if(state){
3493            var g = groups[mi.group];
3494            for(var i = 0, l = g.length; i < l; i++){
3495                if(g[i] != mi){
3496                    g[i].setChecked(false);
3497                }
3498            }
3499        }
3500    }
3501
3502    return {
3503
3504        /**
3505         * Hides all menus that are currently visible
3506         */
3507        hideAll : function(){
3508             hideAll();  
3509        },
3510
3511        // private
3512        register : function(menu){
3513            if(!menus){
3514                init();
3515            }
3516            menus[menu.id] = menu;
3517            menu.on("beforehide", onBeforeHide);
3518            menu.on("hide", onHide);
3519            menu.on("beforeshow", onBeforeShow);
3520            menu.on("show", onShow);
3521            var g = menu.group;
3522            if(g && menu.events["checkchange"]){
3523                if(!groups[g]){
3524                    groups[g] = [];
3525                }
3526                groups[g].push(menu);
3527                menu.on("checkchange", onCheck);
3528            }
3529        },
3530
3531         /**
3532          * Returns a {@link Roo.menu.Menu} object
3533          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3534          * be used to generate and return a new Menu instance.
3535          */
3536        get : function(menu){
3537            if(typeof menu == "string"){ // menu id
3538                return menus[menu];
3539            }else if(menu.events){  // menu instance
3540                return menu;
3541            }
3542            /*else if(typeof menu.length == 'number'){ // array of menu items?
3543                return new Roo.bootstrap.Menu({items:menu});
3544            }else{ // otherwise, must be a config
3545                return new Roo.bootstrap.Menu(menu);
3546            }
3547            */
3548            return false;
3549        },
3550
3551        // private
3552        unregister : function(menu){
3553            delete menus[menu.id];
3554            menu.un("beforehide", onBeforeHide);
3555            menu.un("hide", onHide);
3556            menu.un("beforeshow", onBeforeShow);
3557            menu.un("show", onShow);
3558            var g = menu.group;
3559            if(g && menu.events["checkchange"]){
3560                groups[g].remove(menu);
3561                menu.un("checkchange", onCheck);
3562            }
3563        },
3564
3565        // private
3566        registerCheckable : function(menuItem){
3567            var g = menuItem.group;
3568            if(g){
3569                if(!groups[g]){
3570                    groups[g] = [];
3571                }
3572                groups[g].push(menuItem);
3573                menuItem.on("beforecheckchange", onBeforeCheck);
3574            }
3575        },
3576
3577        // private
3578        unregisterCheckable : function(menuItem){
3579            var g = menuItem.group;
3580            if(g){
3581                groups[g].remove(menuItem);
3582                menuItem.un("beforecheckchange", onBeforeCheck);
3583            }
3584        }
3585    };
3586 }();/*
3587  * - LGPL
3588  *
3589  * menu
3590  * 
3591  */
3592
3593 /**
3594  * @class Roo.bootstrap.Menu
3595  * @extends Roo.bootstrap.Component
3596  * Bootstrap Menu class - container for MenuItems
3597  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3598  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3599  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3600  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3601   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3602   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3603  
3604  * @constructor
3605  * Create a new Menu
3606  * @param {Object} config The config object
3607  */
3608
3609
3610 Roo.bootstrap.Menu = function(config){
3611     
3612     if (config.type == 'treeview') {
3613         // normally menu's are drawn attached to the document to handle layering etc..
3614         // however treeview (used by the docs menu is drawn into the parent element)
3615         this.container_method = 'getChildContainer'; 
3616     }
3617     
3618     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3619     if (this.registerMenu && this.type != 'treeview')  {
3620         Roo.bootstrap.MenuMgr.register(this);
3621     }
3622     
3623     
3624     this.addEvents({
3625         /**
3626          * @event beforeshow
3627          * Fires before this menu is displayed (return false to block)
3628          * @param {Roo.menu.Menu} this
3629          */
3630         beforeshow : true,
3631         /**
3632          * @event beforehide
3633          * Fires before this menu is hidden (return false to block)
3634          * @param {Roo.menu.Menu} this
3635          */
3636         beforehide : true,
3637         /**
3638          * @event show
3639          * Fires after this menu is displayed
3640          * @param {Roo.menu.Menu} this
3641          */
3642         show : true,
3643         /**
3644          * @event hide
3645          * Fires after this menu is hidden
3646          * @param {Roo.menu.Menu} this
3647          */
3648         hide : true,
3649         /**
3650          * @event click
3651          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3652          * @param {Roo.menu.Menu} this
3653          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3654          * @param {Roo.EventObject} e
3655          */
3656         click : true,
3657         /**
3658          * @event mouseover
3659          * Fires when the mouse is hovering over this menu
3660          * @param {Roo.menu.Menu} this
3661          * @param {Roo.EventObject} e
3662          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3663          */
3664         mouseover : true,
3665         /**
3666          * @event mouseout
3667          * Fires when the mouse exits this menu
3668          * @param {Roo.menu.Menu} this
3669          * @param {Roo.EventObject} e
3670          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3671          */
3672         mouseout : true,
3673         /**
3674          * @event itemclick
3675          * Fires when a menu item contained in this menu is clicked
3676          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3677          * @param {Roo.EventObject} e
3678          */
3679         itemclick: true
3680     });
3681     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3682 };
3683
3684 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3685     
3686    /// html : false,
3687    
3688     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3689     type: false,
3690     /**
3691      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3692      */
3693     registerMenu : true,
3694     
3695     menuItems :false, // stores the menu items..
3696     
3697     hidden:true,
3698         
3699     parentMenu : false,
3700     
3701     stopEvent : true,
3702     
3703     isLink : false,
3704     
3705     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3706     
3707     hideTrigger : false,
3708     
3709     align : 'tl-bl?',
3710     
3711     
3712     getChildContainer : function() {
3713         return this.el;  
3714     },
3715     
3716     getAutoCreate : function(){
3717          
3718         //if (['right'].indexOf(this.align)!==-1) {
3719         //    cfg.cn[1].cls += ' pull-right'
3720         //}
3721          
3722         var cfg = {
3723             tag : 'ul',
3724             cls : 'dropdown-menu shadow' ,
3725             style : 'z-index:1000'
3726             
3727         };
3728         
3729         if (this.type === 'submenu') {
3730             cfg.cls = 'submenu active';
3731         }
3732         if (this.type === 'treeview') {
3733             cfg.cls = 'treeview-menu';
3734         }
3735         
3736         return cfg;
3737     },
3738     initEvents : function() {
3739         
3740        // Roo.log("ADD event");
3741        // Roo.log(this.triggerEl.dom);
3742         if (this.triggerEl) {
3743             
3744             this.triggerEl.on('click', this.onTriggerClick, this);
3745             
3746             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3747             
3748             if (!this.hideTrigger) {
3749                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3750                     // dropdown toggle on the 'a' in BS4?
3751                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3752                 } else {
3753                     this.triggerEl.addClass('dropdown-toggle');
3754                 }
3755             }
3756         }
3757         
3758         if (Roo.isTouch) {
3759             this.el.on('touchstart'  , this.onTouch, this);
3760         }
3761         this.el.on('click' , this.onClick, this);
3762
3763         this.el.on("mouseover", this.onMouseOver, this);
3764         this.el.on("mouseout", this.onMouseOut, this);
3765         
3766     },
3767     
3768     findTargetItem : function(e)
3769     {
3770         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3771         if(!t){
3772             return false;
3773         }
3774         //Roo.log(t);         Roo.log(t.id);
3775         if(t && t.id){
3776             //Roo.log(this.menuitems);
3777             return this.menuitems.get(t.id);
3778             
3779             //return this.items.get(t.menuItemId);
3780         }
3781         
3782         return false;
3783     },
3784     
3785     onTouch : function(e) 
3786     {
3787         Roo.log("menu.onTouch");
3788         //e.stopEvent(); this make the user popdown broken
3789         this.onClick(e);
3790     },
3791     
3792     onClick : function(e)
3793     {
3794         Roo.log("menu.onClick");
3795         
3796         var t = this.findTargetItem(e);
3797         if(!t || t.isContainer){
3798             return;
3799         }
3800         Roo.log(e);
3801         /*
3802         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3803             if(t == this.activeItem && t.shouldDeactivate(e)){
3804                 this.activeItem.deactivate();
3805                 delete this.activeItem;
3806                 return;
3807             }
3808             if(t.canActivate){
3809                 this.setActiveItem(t, true);
3810             }
3811             return;
3812             
3813             
3814         }
3815         */
3816        
3817         Roo.log('pass click event');
3818         
3819         t.onClick(e);
3820         
3821         this.fireEvent("click", this, t, e);
3822         
3823         var _this = this;
3824         
3825         if(!t.href.length || t.href == '#'){
3826             (function() { _this.hide(); }).defer(100);
3827         }
3828         
3829     },
3830     
3831     onMouseOver : function(e){
3832         var t  = this.findTargetItem(e);
3833         //Roo.log(t);
3834         //if(t){
3835         //    if(t.canActivate && !t.disabled){
3836         //        this.setActiveItem(t, true);
3837         //    }
3838         //}
3839         
3840         this.fireEvent("mouseover", this, e, t);
3841     },
3842     isVisible : function(){
3843         return !this.hidden;
3844     },
3845     onMouseOut : function(e){
3846         var t  = this.findTargetItem(e);
3847         
3848         //if(t ){
3849         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3850         //        this.activeItem.deactivate();
3851         //        delete this.activeItem;
3852         //    }
3853         //}
3854         this.fireEvent("mouseout", this, e, t);
3855     },
3856     
3857     
3858     /**
3859      * Displays this menu relative to another element
3860      * @param {String/HTMLElement/Roo.Element} element The element to align to
3861      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3862      * the element (defaults to this.defaultAlign)
3863      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3864      */
3865     show : function(el, pos, parentMenu)
3866     {
3867         if (false === this.fireEvent("beforeshow", this)) {
3868             Roo.log("show canceled");
3869             return;
3870         }
3871         this.parentMenu = parentMenu;
3872         if(!this.el){
3873             this.render();
3874         }
3875         this.el.addClass('show'); // show otherwise we do not know how big we are..
3876          
3877         var xy = this.el.getAlignToXY(el, pos);
3878         
3879         // bl-tl << left align  below
3880         // tl-bl << left align 
3881         
3882         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3883             // if it goes to far to the right.. -> align left.
3884             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3885         }
3886         if(xy[0] < 0){
3887             // was left align - go right?
3888             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3889         }
3890         
3891         // goes down the bottom
3892         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3893            xy[1]  < 0 ){
3894             var a = this.align.replace('?', '').split('-');
3895             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3896             
3897         }
3898         
3899         this.showAt(  xy , parentMenu, false);
3900     },
3901      /**
3902      * Displays this menu at a specific xy position
3903      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3904      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3905      */
3906     showAt : function(xy, parentMenu, /* private: */_e){
3907         this.parentMenu = parentMenu;
3908         if(!this.el){
3909             this.render();
3910         }
3911         if(_e !== false){
3912             this.fireEvent("beforeshow", this);
3913             //xy = this.el.adjustForConstraints(xy);
3914         }
3915         
3916         //this.el.show();
3917         this.hideMenuItems();
3918         this.hidden = false;
3919         if (this.triggerEl) {
3920             this.triggerEl.addClass('open');
3921         }
3922         
3923         this.el.addClass('show');
3924         
3925         
3926         
3927         // reassign x when hitting right
3928         
3929         // reassign y when hitting bottom
3930         
3931         // but the list may align on trigger left or trigger top... should it be a properity?
3932         
3933         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3934             this.el.setXY(xy);
3935         }
3936         
3937         this.focus();
3938         this.fireEvent("show", this);
3939     },
3940     
3941     focus : function(){
3942         return;
3943         if(!this.hidden){
3944             this.doFocus.defer(50, this);
3945         }
3946     },
3947
3948     doFocus : function(){
3949         if(!this.hidden){
3950             this.focusEl.focus();
3951         }
3952     },
3953
3954     /**
3955      * Hides this menu and optionally all parent menus
3956      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3957      */
3958     hide : function(deep)
3959     {
3960         if (false === this.fireEvent("beforehide", this)) {
3961             Roo.log("hide canceled");
3962             return;
3963         }
3964         this.hideMenuItems();
3965         if(this.el && this.isVisible()){
3966            
3967             if(this.activeItem){
3968                 this.activeItem.deactivate();
3969                 this.activeItem = null;
3970             }
3971             if (this.triggerEl) {
3972                 this.triggerEl.removeClass('open');
3973             }
3974             
3975             this.el.removeClass('show');
3976             this.hidden = true;
3977             this.fireEvent("hide", this);
3978         }
3979         if(deep === true && this.parentMenu){
3980             this.parentMenu.hide(true);
3981         }
3982     },
3983     
3984     onTriggerClick : function(e)
3985     {
3986         Roo.log('trigger click');
3987         
3988         var target = e.getTarget();
3989         
3990         Roo.log(target.nodeName.toLowerCase());
3991         
3992         if(target.nodeName.toLowerCase() === 'i'){
3993             e.preventDefault();
3994         }
3995         
3996     },
3997     
3998     onTriggerPress  : function(e)
3999     {
4000         Roo.log('trigger press');
4001         //Roo.log(e.getTarget());
4002        // Roo.log(this.triggerEl.dom);
4003        
4004         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4005         var pel = Roo.get(e.getTarget());
4006         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4007             Roo.log('is treeview or dropdown?');
4008             return;
4009         }
4010         
4011         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4012             return;
4013         }
4014         
4015         if (this.isVisible()) {
4016             Roo.log('hide');
4017             this.hide();
4018         } else {
4019             Roo.log('show');
4020             
4021             this.show(this.triggerEl, this.align, false);
4022         }
4023         
4024         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4025             e.stopEvent();
4026         }
4027         
4028     },
4029        
4030     
4031     hideMenuItems : function()
4032     {
4033         Roo.log("hide Menu Items");
4034         if (!this.el) { 
4035             return;
4036         }
4037         
4038         this.el.select('.open',true).each(function(aa) {
4039             
4040             aa.removeClass('open');
4041          
4042         });
4043     },
4044     addxtypeChild : function (tree, cntr) {
4045         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4046           
4047         this.menuitems.add(comp);
4048         return comp;
4049
4050     },
4051     getEl : function()
4052     {
4053         Roo.log(this.el);
4054         return this.el;
4055     },
4056     
4057     clear : function()
4058     {
4059         this.getEl().dom.innerHTML = '';
4060         this.menuitems.clear();
4061     }
4062 });
4063
4064  
4065  /*
4066  * - LGPL
4067  *
4068  * menu item
4069  * 
4070  */
4071
4072
4073 /**
4074  * @class Roo.bootstrap.MenuItem
4075  * @extends Roo.bootstrap.Component
4076  * Bootstrap MenuItem class
4077  * @cfg {String} html the menu label
4078  * @cfg {String} href the link
4079  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4080  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4081  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4082  * @cfg {String} fa favicon to show on left of menu item.
4083  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4084  * 
4085  * 
4086  * @constructor
4087  * Create a new MenuItem
4088  * @param {Object} config The config object
4089  */
4090
4091
4092 Roo.bootstrap.MenuItem = function(config){
4093     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4094     this.addEvents({
4095         // raw events
4096         /**
4097          * @event click
4098          * The raw click event for the entire grid.
4099          * @param {Roo.bootstrap.MenuItem} this
4100          * @param {Roo.EventObject} e
4101          */
4102         "click" : true
4103     });
4104 };
4105
4106 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4107     
4108     href : false,
4109     html : false,
4110     preventDefault: false,
4111     isContainer : false,
4112     active : false,
4113     fa: false,
4114     
4115     getAutoCreate : function(){
4116         
4117         if(this.isContainer){
4118             return {
4119                 tag: 'li',
4120                 cls: 'dropdown-menu-item '
4121             };
4122         }
4123         var ctag = {
4124             tag: 'span',
4125             html: 'Link'
4126         };
4127         
4128         var anc = {
4129             tag : 'a',
4130             cls : 'dropdown-item',
4131             href : '#',
4132             cn : [  ]
4133         };
4134         
4135         if (this.fa !== false) {
4136             anc.cn.push({
4137                 tag : 'i',
4138                 cls : 'fa fa-' + this.fa
4139             });
4140         }
4141         
4142         anc.cn.push(ctag);
4143         
4144         
4145         var cfg= {
4146             tag: 'li',
4147             cls: 'dropdown-menu-item',
4148             cn: [ anc ]
4149         };
4150         if (this.parent().type == 'treeview') {
4151             cfg.cls = 'treeview-menu';
4152         }
4153         if (this.active) {
4154             cfg.cls += ' active';
4155         }
4156         
4157         
4158         
4159         anc.href = this.href || cfg.cn[0].href ;
4160         ctag.html = this.html || cfg.cn[0].html ;
4161         return cfg;
4162     },
4163     
4164     initEvents: function()
4165     {
4166         if (this.parent().type == 'treeview') {
4167             this.el.select('a').on('click', this.onClick, this);
4168         }
4169         
4170         if (this.menu) {
4171             this.menu.parentType = this.xtype;
4172             this.menu.triggerEl = this.el;
4173             this.menu = this.addxtype(Roo.apply({}, this.menu));
4174         }
4175         
4176     },
4177     onClick : function(e)
4178     {
4179         Roo.log('item on click ');
4180         
4181         if(this.preventDefault){
4182             e.preventDefault();
4183         }
4184         //this.parent().hideMenuItems();
4185         
4186         this.fireEvent('click', this, e);
4187     },
4188     getEl : function()
4189     {
4190         return this.el;
4191     } 
4192 });
4193
4194  
4195
4196  /*
4197  * - LGPL
4198  *
4199  * menu separator
4200  * 
4201  */
4202
4203
4204 /**
4205  * @class Roo.bootstrap.MenuSeparator
4206  * @extends Roo.bootstrap.Component
4207  * Bootstrap MenuSeparator class
4208  * 
4209  * @constructor
4210  * Create a new MenuItem
4211  * @param {Object} config The config object
4212  */
4213
4214
4215 Roo.bootstrap.MenuSeparator = function(config){
4216     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4217 };
4218
4219 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4220     
4221     getAutoCreate : function(){
4222         var cfg = {
4223             cls: 'divider',
4224             tag : 'li'
4225         };
4226         
4227         return cfg;
4228     }
4229    
4230 });
4231
4232  
4233
4234  
4235 /*
4236 * Licence: LGPL
4237 */
4238
4239 /**
4240  * @class Roo.bootstrap.Modal
4241  * @extends Roo.bootstrap.Component
4242  * Bootstrap Modal class
4243  * @cfg {String} title Title of dialog
4244  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4245  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4246  * @cfg {Boolean} specificTitle default false
4247  * @cfg {Array} buttons Array of buttons or standard button set..
4248  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4249  * @cfg {Boolean} animate default true
4250  * @cfg {Boolean} allow_close default true
4251  * @cfg {Boolean} fitwindow default false
4252  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4253  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4254  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4255  * @cfg {String} size (sm|lg|xl) default empty
4256  * @cfg {Number} max_width set the max width of modal
4257  * @cfg {Boolean} editableTitle can the title be edited
4258
4259  *
4260  *
4261  * @constructor
4262  * Create a new Modal Dialog
4263  * @param {Object} config The config object
4264  */
4265
4266 Roo.bootstrap.Modal = function(config){
4267     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4268     this.addEvents({
4269         // raw events
4270         /**
4271          * @event btnclick
4272          * The raw btnclick event for the button
4273          * @param {Roo.EventObject} e
4274          */
4275         "btnclick" : true,
4276         /**
4277          * @event resize
4278          * Fire when dialog resize
4279          * @param {Roo.bootstrap.Modal} this
4280          * @param {Roo.EventObject} e
4281          */
4282         "resize" : true,
4283         /**
4284          * @event titlechanged
4285          * Fire when the editable title has been changed
4286          * @param {Roo.bootstrap.Modal} this
4287          * @param {Roo.EventObject} value
4288          */
4289         "titlechanged" : true 
4290         
4291     });
4292     this.buttons = this.buttons || [];
4293
4294     if (this.tmpl) {
4295         this.tmpl = Roo.factory(this.tmpl);
4296     }
4297
4298 };
4299
4300 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4301
4302     title : 'test dialog',
4303
4304     buttons : false,
4305
4306     // set on load...
4307
4308     html: false,
4309
4310     tmp: false,
4311
4312     specificTitle: false,
4313
4314     buttonPosition: 'right',
4315
4316     allow_close : true,
4317
4318     animate : true,
4319
4320     fitwindow: false,
4321     
4322      // private
4323     dialogEl: false,
4324     bodyEl:  false,
4325     footerEl:  false,
4326     titleEl:  false,
4327     closeEl:  false,
4328
4329     size: '',
4330     
4331     max_width: 0,
4332     
4333     max_height: 0,
4334     
4335     fit_content: false,
4336     editableTitle  : false,
4337
4338     onRender : function(ct, position)
4339     {
4340         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4341
4342         if(!this.el){
4343             var cfg = Roo.apply({},  this.getAutoCreate());
4344             cfg.id = Roo.id();
4345             //if(!cfg.name){
4346             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4347             //}
4348             //if (!cfg.name.length) {
4349             //    delete cfg.name;
4350            // }
4351             if (this.cls) {
4352                 cfg.cls += ' ' + this.cls;
4353             }
4354             if (this.style) {
4355                 cfg.style = this.style;
4356             }
4357             this.el = Roo.get(document.body).createChild(cfg, position);
4358         }
4359         //var type = this.el.dom.type;
4360
4361
4362         if(this.tabIndex !== undefined){
4363             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4364         }
4365
4366         this.dialogEl = this.el.select('.modal-dialog',true).first();
4367         this.bodyEl = this.el.select('.modal-body',true).first();
4368         this.closeEl = this.el.select('.modal-header .close', true).first();
4369         this.headerEl = this.el.select('.modal-header',true).first();
4370         this.titleEl = this.el.select('.modal-title',true).first();
4371         this.footerEl = this.el.select('.modal-footer',true).first();
4372
4373         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4374         
4375         //this.el.addClass("x-dlg-modal");
4376
4377         if (this.buttons.length) {
4378             Roo.each(this.buttons, function(bb) {
4379                 var b = Roo.apply({}, bb);
4380                 b.xns = b.xns || Roo.bootstrap;
4381                 b.xtype = b.xtype || 'Button';
4382                 if (typeof(b.listeners) == 'undefined') {
4383                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4384                 }
4385
4386                 var btn = Roo.factory(b);
4387
4388                 btn.render(this.getButtonContainer());
4389
4390             },this);
4391         }
4392         // render the children.
4393         var nitems = [];
4394
4395         if(typeof(this.items) != 'undefined'){
4396             var items = this.items;
4397             delete this.items;
4398
4399             for(var i =0;i < items.length;i++) {
4400                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4401             }
4402         }
4403
4404         this.items = nitems;
4405
4406         // where are these used - they used to be body/close/footer
4407
4408
4409         this.initEvents();
4410         //this.el.addClass([this.fieldClass, this.cls]);
4411
4412     },
4413
4414     getAutoCreate : function()
4415     {
4416         // we will default to modal-body-overflow - might need to remove or make optional later.
4417         var bdy = {
4418                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4419                 html : this.html || ''
4420         };
4421
4422         var title = {
4423             tag: 'h5',
4424             cls : 'modal-title',
4425             html : this.title
4426         };
4427
4428         if(this.specificTitle){ // WTF is this?
4429             title = this.title;
4430         }
4431
4432         var header = [];
4433         if (this.allow_close && Roo.bootstrap.version == 3) {
4434             header.push({
4435                 tag: 'button',
4436                 cls : 'close',
4437                 html : '&times'
4438             });
4439         }
4440
4441         header.push(title);
4442
4443         if (this.editableTitle) {
4444             header.push({
4445                 cls: 'form-control roo-editable-title d-none',
4446                 tag: 'input',
4447                 type: 'text'
4448             });
4449         }
4450         
4451         if (this.allow_close && Roo.bootstrap.version == 4) {
4452             header.push({
4453                 tag: 'button',
4454                 cls : 'close',
4455                 html : '&times'
4456             });
4457         }
4458         
4459         var size = '';
4460
4461         if(this.size.length){
4462             size = 'modal-' + this.size;
4463         }
4464         
4465         var footer = Roo.bootstrap.version == 3 ?
4466             {
4467                 cls : 'modal-footer',
4468                 cn : [
4469                     {
4470                         tag: 'div',
4471                         cls: 'btn-' + this.buttonPosition
4472                     }
4473                 ]
4474
4475             } :
4476             {  // BS4 uses mr-auto on left buttons....
4477                 cls : 'modal-footer'
4478             };
4479
4480             
4481
4482         
4483         
4484         var modal = {
4485             cls: "modal",
4486              cn : [
4487                 {
4488                     cls: "modal-dialog " + size,
4489                     cn : [
4490                         {
4491                             cls : "modal-content",
4492                             cn : [
4493                                 {
4494                                     cls : 'modal-header',
4495                                     cn : header
4496                                 },
4497                                 bdy,
4498                                 footer
4499                             ]
4500
4501                         }
4502                     ]
4503
4504                 }
4505             ]
4506         };
4507
4508         if(this.animate){
4509             modal.cls += ' fade';
4510         }
4511
4512         return modal;
4513
4514     },
4515     getChildContainer : function() {
4516
4517          return this.bodyEl;
4518
4519     },
4520     getButtonContainer : function() {
4521         
4522          return Roo.bootstrap.version == 4 ?
4523             this.el.select('.modal-footer',true).first()
4524             : this.el.select('.modal-footer div',true).first();
4525
4526     },
4527     initEvents : function()
4528     {
4529         if (this.allow_close) {
4530             this.closeEl.on('click', this.hide, this);
4531         }
4532         Roo.EventManager.onWindowResize(this.resize, this, true);
4533         if (this.editableTitle) {
4534             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4535             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4536             this.headerEditEl.on('keyup', function(e) {
4537                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4538                         this.toggleHeaderInput(false)
4539                     }
4540                 }, this);
4541             this.headerEditEl.on('blur', function(e) {
4542                 this.toggleHeaderInput(false)
4543             },this);
4544         }
4545
4546     },
4547   
4548
4549     resize : function()
4550     {
4551         this.maskEl.setSize(
4552             Roo.lib.Dom.getViewWidth(true),
4553             Roo.lib.Dom.getViewHeight(true)
4554         );
4555         
4556         if (this.fitwindow) {
4557             
4558            this.dialogEl.setStyle( { 'max-width' : '100%' });
4559             this.setSize(
4560                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4561                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4562             );
4563             return;
4564         }
4565         
4566         if(this.max_width !== 0) {
4567             
4568             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4569             
4570             if(this.height) {
4571                 this.setSize(w, this.height);
4572                 return;
4573             }
4574             
4575             if(this.max_height) {
4576                 this.setSize(w,Math.min(
4577                     this.max_height,
4578                     Roo.lib.Dom.getViewportHeight(true) - 60
4579                 ));
4580                 
4581                 return;
4582             }
4583             
4584             if(!this.fit_content) {
4585                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4586                 return;
4587             }
4588             
4589             this.setSize(w, Math.min(
4590                 60 +
4591                 this.headerEl.getHeight() + 
4592                 this.footerEl.getHeight() + 
4593                 this.getChildHeight(this.bodyEl.dom.childNodes),
4594                 Roo.lib.Dom.getViewportHeight(true) - 60)
4595             );
4596         }
4597         
4598     },
4599
4600     setSize : function(w,h)
4601     {
4602         if (!w && !h) {
4603             return;
4604         }
4605         
4606         this.resizeTo(w,h);
4607     },
4608
4609     show : function() {
4610
4611         if (!this.rendered) {
4612             this.render();
4613         }
4614         this.toggleHeaderInput(false);
4615         //this.el.setStyle('display', 'block');
4616         this.el.removeClass('hideing');
4617         this.el.dom.style.display='block';
4618         
4619         Roo.get(document.body).addClass('modal-open');
4620  
4621         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4622             
4623             (function(){
4624                 this.el.addClass('show');
4625                 this.el.addClass('in');
4626             }).defer(50, this);
4627         }else{
4628             this.el.addClass('show');
4629             this.el.addClass('in');
4630         }
4631
4632         // not sure how we can show data in here..
4633         //if (this.tmpl) {
4634         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4635         //}
4636
4637         Roo.get(document.body).addClass("x-body-masked");
4638         
4639         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4640         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4641         this.maskEl.dom.style.display = 'block';
4642         this.maskEl.addClass('show');
4643         
4644         
4645         this.resize();
4646         
4647         this.fireEvent('show', this);
4648
4649         // set zindex here - otherwise it appears to be ignored...
4650         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4651
4652         (function () {
4653             this.items.forEach( function(e) {
4654                 e.layout ? e.layout() : false;
4655
4656             });
4657         }).defer(100,this);
4658
4659     },
4660     hide : function()
4661     {
4662         if(this.fireEvent("beforehide", this) !== false){
4663             
4664             this.maskEl.removeClass('show');
4665             
4666             this.maskEl.dom.style.display = '';
4667             Roo.get(document.body).removeClass("x-body-masked");
4668             this.el.removeClass('in');
4669             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4670
4671             if(this.animate){ // why
4672                 this.el.addClass('hideing');
4673                 this.el.removeClass('show');
4674                 (function(){
4675                     if (!this.el.hasClass('hideing')) {
4676                         return; // it's been shown again...
4677                     }
4678                     
4679                     this.el.dom.style.display='';
4680
4681                     Roo.get(document.body).removeClass('modal-open');
4682                     this.el.removeClass('hideing');
4683                 }).defer(150,this);
4684                 
4685             }else{
4686                 this.el.removeClass('show');
4687                 this.el.dom.style.display='';
4688                 Roo.get(document.body).removeClass('modal-open');
4689
4690             }
4691             this.fireEvent('hide', this);
4692         }
4693     },
4694     isVisible : function()
4695     {
4696         
4697         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4698         
4699     },
4700
4701     addButton : function(str, cb)
4702     {
4703
4704
4705         var b = Roo.apply({}, { html : str } );
4706         b.xns = b.xns || Roo.bootstrap;
4707         b.xtype = b.xtype || 'Button';
4708         if (typeof(b.listeners) == 'undefined') {
4709             b.listeners = { click : cb.createDelegate(this)  };
4710         }
4711
4712         var btn = Roo.factory(b);
4713
4714         btn.render(this.getButtonContainer());
4715
4716         return btn;
4717
4718     },
4719
4720     setDefaultButton : function(btn)
4721     {
4722         //this.el.select('.modal-footer').()
4723     },
4724
4725     resizeTo: function(w,h)
4726     {
4727         this.dialogEl.setWidth(w);
4728         
4729         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4730
4731         this.bodyEl.setHeight(h - diff);
4732         
4733         this.fireEvent('resize', this);
4734     },
4735     
4736     setContentSize  : function(w, h)
4737     {
4738
4739     },
4740     onButtonClick: function(btn,e)
4741     {
4742         //Roo.log([a,b,c]);
4743         this.fireEvent('btnclick', btn.name, e);
4744     },
4745      /**
4746      * Set the title of the Dialog
4747      * @param {String} str new Title
4748      */
4749     setTitle: function(str) {
4750         this.titleEl.dom.innerHTML = str;
4751         this.title = str;
4752     },
4753     /**
4754      * Set the body of the Dialog
4755      * @param {String} str new Title
4756      */
4757     setBody: function(str) {
4758         this.bodyEl.dom.innerHTML = str;
4759     },
4760     /**
4761      * Set the body of the Dialog using the template
4762      * @param {Obj} data - apply this data to the template and replace the body contents.
4763      */
4764     applyBody: function(obj)
4765     {
4766         if (!this.tmpl) {
4767             Roo.log("Error - using apply Body without a template");
4768             //code
4769         }
4770         this.tmpl.overwrite(this.bodyEl, obj);
4771     },
4772     
4773     getChildHeight : function(child_nodes)
4774     {
4775         if(
4776             !child_nodes ||
4777             child_nodes.length == 0
4778         ) {
4779             return 0;
4780         }
4781         
4782         var child_height = 0;
4783         
4784         for(var i = 0; i < child_nodes.length; i++) {
4785             
4786             /*
4787             * for modal with tabs...
4788             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4789                 
4790                 var layout_childs = child_nodes[i].childNodes;
4791                 
4792                 for(var j = 0; j < layout_childs.length; j++) {
4793                     
4794                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4795                         
4796                         var layout_body_childs = layout_childs[j].childNodes;
4797                         
4798                         for(var k = 0; k < layout_body_childs.length; k++) {
4799                             
4800                             if(layout_body_childs[k].classList.contains('navbar')) {
4801                                 child_height += layout_body_childs[k].offsetHeight;
4802                                 continue;
4803                             }
4804                             
4805                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4806                                 
4807                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4808                                 
4809                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4810                                     
4811                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4812                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4813                                         continue;
4814                                     }
4815                                     
4816                                 }
4817                                 
4818                             }
4819                             
4820                         }
4821                     }
4822                 }
4823                 continue;
4824             }
4825             */
4826             
4827             child_height += child_nodes[i].offsetHeight;
4828             // Roo.log(child_nodes[i].offsetHeight);
4829         }
4830         
4831         return child_height;
4832     },
4833     toggleHeaderInput : function(is_edit)
4834     {
4835         if (!this.editableTitle) {
4836             return; // not editable.
4837         }
4838         if (is_edit && this.is_header_editing) {
4839             return; // already editing..
4840         }
4841         if (is_edit) {
4842     
4843             this.headerEditEl.dom.value = this.title;
4844             this.headerEditEl.removeClass('d-none');
4845             this.headerEditEl.dom.focus();
4846             this.titleEl.addClass('d-none');
4847             
4848             this.is_header_editing = true;
4849             return
4850         }
4851         // flip back to not editing.
4852         this.title = this.headerEditEl.dom.value;
4853         this.headerEditEl.addClass('d-none');
4854         this.titleEl.removeClass('d-none');
4855         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4856         this.is_header_editing = false;
4857         this.fireEvent('titlechanged', this, this.title);
4858     
4859             
4860         
4861     }
4862
4863 });
4864
4865
4866 Roo.apply(Roo.bootstrap.Modal,  {
4867     /**
4868          * Button config that displays a single OK button
4869          * @type Object
4870          */
4871         OK :  [{
4872             name : 'ok',
4873             weight : 'primary',
4874             html : 'OK'
4875         }],
4876         /**
4877          * Button config that displays Yes and No buttons
4878          * @type Object
4879          */
4880         YESNO : [
4881             {
4882                 name  : 'no',
4883                 html : 'No'
4884             },
4885             {
4886                 name  :'yes',
4887                 weight : 'primary',
4888                 html : 'Yes'
4889             }
4890         ],
4891
4892         /**
4893          * Button config that displays OK and Cancel buttons
4894          * @type Object
4895          */
4896         OKCANCEL : [
4897             {
4898                name : 'cancel',
4899                 html : 'Cancel'
4900             },
4901             {
4902                 name : 'ok',
4903                 weight : 'primary',
4904                 html : 'OK'
4905             }
4906         ],
4907         /**
4908          * Button config that displays Yes, No and Cancel buttons
4909          * @type Object
4910          */
4911         YESNOCANCEL : [
4912             {
4913                 name : 'yes',
4914                 weight : 'primary',
4915                 html : 'Yes'
4916             },
4917             {
4918                 name : 'no',
4919                 html : 'No'
4920             },
4921             {
4922                 name : 'cancel',
4923                 html : 'Cancel'
4924             }
4925         ],
4926         
4927         zIndex : 10001
4928 });
4929
4930 /*
4931  * - LGPL
4932  *
4933  * messagebox - can be used as a replace
4934  * 
4935  */
4936 /**
4937  * @class Roo.MessageBox
4938  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4939  * Example usage:
4940  *<pre><code>
4941 // Basic alert:
4942 Roo.Msg.alert('Status', 'Changes saved successfully.');
4943
4944 // Prompt for user data:
4945 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4946     if (btn == 'ok'){
4947         // process text value...
4948     }
4949 });
4950
4951 // Show a dialog using config options:
4952 Roo.Msg.show({
4953    title:'Save Changes?',
4954    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4955    buttons: Roo.Msg.YESNOCANCEL,
4956    fn: processResult,
4957    animEl: 'elId'
4958 });
4959 </code></pre>
4960  * @singleton
4961  */
4962 Roo.bootstrap.MessageBox = function(){
4963     var dlg, opt, mask, waitTimer;
4964     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4965     var buttons, activeTextEl, bwidth;
4966
4967     
4968     // private
4969     var handleButton = function(button){
4970         dlg.hide();
4971         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4972     };
4973
4974     // private
4975     var handleHide = function(){
4976         if(opt && opt.cls){
4977             dlg.el.removeClass(opt.cls);
4978         }
4979         //if(waitTimer){
4980         //    Roo.TaskMgr.stop(waitTimer);
4981         //    waitTimer = null;
4982         //}
4983     };
4984
4985     // private
4986     var updateButtons = function(b){
4987         var width = 0;
4988         if(!b){
4989             buttons["ok"].hide();
4990             buttons["cancel"].hide();
4991             buttons["yes"].hide();
4992             buttons["no"].hide();
4993             dlg.footerEl.hide();
4994             
4995             return width;
4996         }
4997         dlg.footerEl.show();
4998         for(var k in buttons){
4999             if(typeof buttons[k] != "function"){
5000                 if(b[k]){
5001                     buttons[k].show();
5002                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5003                     width += buttons[k].el.getWidth()+15;
5004                 }else{
5005                     buttons[k].hide();
5006                 }
5007             }
5008         }
5009         return width;
5010     };
5011
5012     // private
5013     var handleEsc = function(d, k, e){
5014         if(opt && opt.closable !== false){
5015             dlg.hide();
5016         }
5017         if(e){
5018             e.stopEvent();
5019         }
5020     };
5021
5022     return {
5023         /**
5024          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5025          * @return {Roo.BasicDialog} The BasicDialog element
5026          */
5027         getDialog : function(){
5028            if(!dlg){
5029                 dlg = new Roo.bootstrap.Modal( {
5030                     //draggable: true,
5031                     //resizable:false,
5032                     //constraintoviewport:false,
5033                     //fixedcenter:true,
5034                     //collapsible : false,
5035                     //shim:true,
5036                     //modal: true,
5037                 //    width: 'auto',
5038                   //  height:100,
5039                     //buttonAlign:"center",
5040                     closeClick : function(){
5041                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5042                             handleButton("no");
5043                         }else{
5044                             handleButton("cancel");
5045                         }
5046                     }
5047                 });
5048                 dlg.render();
5049                 dlg.on("hide", handleHide);
5050                 mask = dlg.mask;
5051                 //dlg.addKeyListener(27, handleEsc);
5052                 buttons = {};
5053                 this.buttons = buttons;
5054                 var bt = this.buttonText;
5055                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5056                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5057                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5058                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5059                 //Roo.log(buttons);
5060                 bodyEl = dlg.bodyEl.createChild({
5061
5062                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5063                         '<textarea class="roo-mb-textarea"></textarea>' +
5064                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5065                 });
5066                 msgEl = bodyEl.dom.firstChild;
5067                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5068                 textboxEl.enableDisplayMode();
5069                 textboxEl.addKeyListener([10,13], function(){
5070                     if(dlg.isVisible() && opt && opt.buttons){
5071                         if(opt.buttons.ok){
5072                             handleButton("ok");
5073                         }else if(opt.buttons.yes){
5074                             handleButton("yes");
5075                         }
5076                     }
5077                 });
5078                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5079                 textareaEl.enableDisplayMode();
5080                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5081                 progressEl.enableDisplayMode();
5082                 
5083                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5084                 var pf = progressEl.dom.firstChild;
5085                 if (pf) {
5086                     pp = Roo.get(pf.firstChild);
5087                     pp.setHeight(pf.offsetHeight);
5088                 }
5089                 
5090             }
5091             return dlg;
5092         },
5093
5094         /**
5095          * Updates the message box body text
5096          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5097          * the XHTML-compliant non-breaking space character '&amp;#160;')
5098          * @return {Roo.MessageBox} This message box
5099          */
5100         updateText : function(text)
5101         {
5102             if(!dlg.isVisible() && !opt.width){
5103                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5104                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5105             }
5106             msgEl.innerHTML = text || '&#160;';
5107       
5108             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5109             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5110             var w = Math.max(
5111                     Math.min(opt.width || cw , this.maxWidth), 
5112                     Math.max(opt.minWidth || this.minWidth, bwidth)
5113             );
5114             if(opt.prompt){
5115                 activeTextEl.setWidth(w);
5116             }
5117             if(dlg.isVisible()){
5118                 dlg.fixedcenter = false;
5119             }
5120             // to big, make it scroll. = But as usual stupid IE does not support
5121             // !important..
5122             
5123             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5124                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5125                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5126             } else {
5127                 bodyEl.dom.style.height = '';
5128                 bodyEl.dom.style.overflowY = '';
5129             }
5130             if (cw > w) {
5131                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5132             } else {
5133                 bodyEl.dom.style.overflowX = '';
5134             }
5135             
5136             dlg.setContentSize(w, bodyEl.getHeight());
5137             if(dlg.isVisible()){
5138                 dlg.fixedcenter = true;
5139             }
5140             return this;
5141         },
5142
5143         /**
5144          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5145          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5146          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5147          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5148          * @return {Roo.MessageBox} This message box
5149          */
5150         updateProgress : function(value, text){
5151             if(text){
5152                 this.updateText(text);
5153             }
5154             
5155             if (pp) { // weird bug on my firefox - for some reason this is not defined
5156                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5157                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5158             }
5159             return this;
5160         },        
5161
5162         /**
5163          * Returns true if the message box is currently displayed
5164          * @return {Boolean} True if the message box is visible, else false
5165          */
5166         isVisible : function(){
5167             return dlg && dlg.isVisible();  
5168         },
5169
5170         /**
5171          * Hides the message box if it is displayed
5172          */
5173         hide : function(){
5174             if(this.isVisible()){
5175                 dlg.hide();
5176             }  
5177         },
5178
5179         /**
5180          * Displays a new message box, or reinitializes an existing message box, based on the config options
5181          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5182          * The following config object properties are supported:
5183          * <pre>
5184 Property    Type             Description
5185 ----------  ---------------  ------------------------------------------------------------------------------------
5186 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5187                                    closes (defaults to undefined)
5188 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5189                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5190 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5191                                    progress and wait dialogs will ignore this property and always hide the
5192                                    close button as they can only be closed programmatically.
5193 cls               String           A custom CSS class to apply to the message box element
5194 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5195                                    displayed (defaults to 75)
5196 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5197                                    function will be btn (the name of the button that was clicked, if applicable,
5198                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5199                                    Progress and wait dialogs will ignore this option since they do not respond to
5200                                    user actions and can only be closed programmatically, so any required function
5201                                    should be called by the same code after it closes the dialog.
5202 icon              String           A CSS class that provides a background image to be used as an icon for
5203                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5204 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5205 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5206 modal             Boolean          False to allow user interaction with the page while the message box is
5207                                    displayed (defaults to true)
5208 msg               String           A string that will replace the existing message box body text (defaults
5209                                    to the XHTML-compliant non-breaking space character '&#160;')
5210 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5211 progress          Boolean          True to display a progress bar (defaults to false)
5212 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5213 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5214 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5215 title             String           The title text
5216 value             String           The string value to set into the active textbox element if displayed
5217 wait              Boolean          True to display a progress bar (defaults to false)
5218 width             Number           The width of the dialog in pixels
5219 </pre>
5220          *
5221          * Example usage:
5222          * <pre><code>
5223 Roo.Msg.show({
5224    title: 'Address',
5225    msg: 'Please enter your address:',
5226    width: 300,
5227    buttons: Roo.MessageBox.OKCANCEL,
5228    multiline: true,
5229    fn: saveAddress,
5230    animEl: 'addAddressBtn'
5231 });
5232 </code></pre>
5233          * @param {Object} config Configuration options
5234          * @return {Roo.MessageBox} This message box
5235          */
5236         show : function(options)
5237         {
5238             
5239             // this causes nightmares if you show one dialog after another
5240             // especially on callbacks..
5241              
5242             if(this.isVisible()){
5243                 
5244                 this.hide();
5245                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5246                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5247                 Roo.log("New Dialog Message:" +  options.msg )
5248                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5249                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5250                 
5251             }
5252             var d = this.getDialog();
5253             opt = options;
5254             d.setTitle(opt.title || "&#160;");
5255             d.closeEl.setDisplayed(opt.closable !== false);
5256             activeTextEl = textboxEl;
5257             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5258             if(opt.prompt){
5259                 if(opt.multiline){
5260                     textboxEl.hide();
5261                     textareaEl.show();
5262                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5263                         opt.multiline : this.defaultTextHeight);
5264                     activeTextEl = textareaEl;
5265                 }else{
5266                     textboxEl.show();
5267                     textareaEl.hide();
5268                 }
5269             }else{
5270                 textboxEl.hide();
5271                 textareaEl.hide();
5272             }
5273             progressEl.setDisplayed(opt.progress === true);
5274             if (opt.progress) {
5275                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5276             }
5277             this.updateProgress(0);
5278             activeTextEl.dom.value = opt.value || "";
5279             if(opt.prompt){
5280                 dlg.setDefaultButton(activeTextEl);
5281             }else{
5282                 var bs = opt.buttons;
5283                 var db = null;
5284                 if(bs && bs.ok){
5285                     db = buttons["ok"];
5286                 }else if(bs && bs.yes){
5287                     db = buttons["yes"];
5288                 }
5289                 dlg.setDefaultButton(db);
5290             }
5291             bwidth = updateButtons(opt.buttons);
5292             this.updateText(opt.msg);
5293             if(opt.cls){
5294                 d.el.addClass(opt.cls);
5295             }
5296             d.proxyDrag = opt.proxyDrag === true;
5297             d.modal = opt.modal !== false;
5298             d.mask = opt.modal !== false ? mask : false;
5299             if(!d.isVisible()){
5300                 // force it to the end of the z-index stack so it gets a cursor in FF
5301                 document.body.appendChild(dlg.el.dom);
5302                 d.animateTarget = null;
5303                 d.show(options.animEl);
5304             }
5305             return this;
5306         },
5307
5308         /**
5309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5311          * and closing the message box when the process is complete.
5312          * @param {String} title The title bar text
5313          * @param {String} msg The message box body text
5314          * @return {Roo.MessageBox} This message box
5315          */
5316         progress : function(title, msg){
5317             this.show({
5318                 title : title,
5319                 msg : msg,
5320                 buttons: false,
5321                 progress:true,
5322                 closable:false,
5323                 minWidth: this.minProgressWidth,
5324                 modal : true
5325             });
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5331          * If a callback function is passed it will be called after the user clicks the button, and the
5332          * id of the button that was clicked will be passed as the only parameter to the callback
5333          * (could also be the top-right close button).
5334          * @param {String} title The title bar text
5335          * @param {String} msg The message box body text
5336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5337          * @param {Object} scope (optional) The scope of the callback function
5338          * @return {Roo.MessageBox} This message box
5339          */
5340         alert : function(title, msg, fn, scope)
5341         {
5342             this.show({
5343                 title : title,
5344                 msg : msg,
5345                 buttons: this.OK,
5346                 fn: fn,
5347                 closable : false,
5348                 scope : scope,
5349                 modal : true
5350             });
5351             return this;
5352         },
5353
5354         /**
5355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5357          * You are responsible for closing the message box when the process is complete.
5358          * @param {String} msg The message box body text
5359          * @param {String} title (optional) The title bar text
5360          * @return {Roo.MessageBox} This message box
5361          */
5362         wait : function(msg, title){
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: false,
5367                 closable:false,
5368                 progress:true,
5369                 modal:true,
5370                 width:300,
5371                 wait:true
5372             });
5373             waitTimer = Roo.TaskMgr.start({
5374                 run: function(i){
5375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5376                 },
5377                 interval: 1000
5378             });
5379             return this;
5380         },
5381
5382         /**
5383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5386          * @param {String} title The title bar text
5387          * @param {String} msg The message box body text
5388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5389          * @param {Object} scope (optional) The scope of the callback function
5390          * @return {Roo.MessageBox} This message box
5391          */
5392         confirm : function(title, msg, fn, scope){
5393             this.show({
5394                 title : title,
5395                 msg : msg,
5396                 buttons: this.YESNO,
5397                 fn: fn,
5398                 scope : scope,
5399                 modal : true
5400             });
5401             return this;
5402         },
5403
5404         /**
5405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5408          * (could also be the top-right close button) and the text that was entered will be passed as the two
5409          * parameters to the callback.
5410          * @param {String} title The title bar text
5411          * @param {String} msg The message box body text
5412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5413          * @param {Object} scope (optional) The scope of the callback function
5414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5416          * @return {Roo.MessageBox} This message box
5417          */
5418         prompt : function(title, msg, fn, scope, multiline){
5419             this.show({
5420                 title : title,
5421                 msg : msg,
5422                 buttons: this.OKCANCEL,
5423                 fn: fn,
5424                 minWidth:250,
5425                 scope : scope,
5426                 prompt:true,
5427                 multiline: multiline,
5428                 modal : true
5429             });
5430             return this;
5431         },
5432
5433         /**
5434          * Button config that displays a single OK button
5435          * @type Object
5436          */
5437         OK : {ok:true},
5438         /**
5439          * Button config that displays Yes and No buttons
5440          * @type Object
5441          */
5442         YESNO : {yes:true, no:true},
5443         /**
5444          * Button config that displays OK and Cancel buttons
5445          * @type Object
5446          */
5447         OKCANCEL : {ok:true, cancel:true},
5448         /**
5449          * Button config that displays Yes, No and Cancel buttons
5450          * @type Object
5451          */
5452         YESNOCANCEL : {yes:true, no:true, cancel:true},
5453
5454         /**
5455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5456          * @type Number
5457          */
5458         defaultTextHeight : 75,
5459         /**
5460          * The maximum width in pixels of the message box (defaults to 600)
5461          * @type Number
5462          */
5463         maxWidth : 600,
5464         /**
5465          * The minimum width in pixels of the message box (defaults to 100)
5466          * @type Number
5467          */
5468         minWidth : 100,
5469         /**
5470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5472          * @type Number
5473          */
5474         minProgressWidth : 250,
5475         /**
5476          * An object containing the default button text strings that can be overriden for localized language support.
5477          * Supported properties are: ok, cancel, yes and no.
5478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5479          * @type Object
5480          */
5481         buttonText : {
5482             ok : "OK",
5483             cancel : "Cancel",
5484             yes : "Yes",
5485             no : "No"
5486         }
5487     };
5488 }();
5489
5490 /**
5491  * Shorthand for {@link Roo.MessageBox}
5492  */
5493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5494 Roo.Msg = Roo.Msg || Roo.MessageBox;
5495 /*
5496  * - LGPL
5497  *
5498  * navbar
5499  * 
5500  */
5501
5502 /**
5503  * @class Roo.bootstrap.Navbar
5504  * @extends Roo.bootstrap.Component
5505  * Bootstrap Navbar class
5506
5507  * @constructor
5508  * Create a new Navbar
5509  * @param {Object} config The config object
5510  */
5511
5512
5513 Roo.bootstrap.Navbar = function(config){
5514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5515     this.addEvents({
5516         // raw events
5517         /**
5518          * @event beforetoggle
5519          * Fire before toggle the menu
5520          * @param {Roo.EventObject} e
5521          */
5522         "beforetoggle" : true
5523     });
5524 };
5525
5526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5527     
5528     
5529    
5530     // private
5531     navItems : false,
5532     loadMask : false,
5533     
5534     
5535     getAutoCreate : function(){
5536         
5537         
5538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5539         
5540     },
5541     
5542     initEvents :function ()
5543     {
5544         //Roo.log(this.el.select('.navbar-toggle',true));
5545         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5546         
5547         var mark = {
5548             tag: "div",
5549             cls:"x-dlg-mask"
5550         };
5551         
5552         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5553         
5554         var size = this.el.getSize();
5555         this.maskEl.setSize(size.width, size.height);
5556         this.maskEl.enableDisplayMode("block");
5557         this.maskEl.hide();
5558         
5559         if(this.loadMask){
5560             this.maskEl.show();
5561         }
5562     },
5563     
5564     
5565     getChildContainer : function()
5566     {
5567         if (this.el && this.el.select('.collapse').getCount()) {
5568             return this.el.select('.collapse',true).first();
5569         }
5570         
5571         return this.el;
5572     },
5573     
5574     mask : function()
5575     {
5576         this.maskEl.show();
5577     },
5578     
5579     unmask : function()
5580     {
5581         this.maskEl.hide();
5582     },
5583     onToggle : function()
5584     {
5585         
5586         if(this.fireEvent('beforetoggle', this) === false){
5587             return;
5588         }
5589         var ce = this.el.select('.navbar-collapse',true).first();
5590       
5591         if (!ce.hasClass('show')) {
5592            this.expand();
5593         } else {
5594             this.collapse();
5595         }
5596         
5597         
5598     
5599     },
5600     /**
5601      * Expand the navbar pulldown 
5602      */
5603     expand : function ()
5604     {
5605        
5606         var ce = this.el.select('.navbar-collapse',true).first();
5607         if (ce.hasClass('collapsing')) {
5608             return;
5609         }
5610         ce.dom.style.height = '';
5611                // show it...
5612         ce.addClass('in'); // old...
5613         ce.removeClass('collapse');
5614         ce.addClass('show');
5615         var h = ce.getHeight();
5616         Roo.log(h);
5617         ce.removeClass('show');
5618         // at this point we should be able to see it..
5619         ce.addClass('collapsing');
5620         
5621         ce.setHeight(0); // resize it ...
5622         ce.on('transitionend', function() {
5623             //Roo.log('done transition');
5624             ce.removeClass('collapsing');
5625             ce.addClass('show');
5626             ce.removeClass('collapse');
5627
5628             ce.dom.style.height = '';
5629         }, this, { single: true} );
5630         ce.setHeight(h);
5631         ce.dom.scrollTop = 0;
5632     },
5633     /**
5634      * Collapse the navbar pulldown 
5635      */
5636     collapse : function()
5637     {
5638          var ce = this.el.select('.navbar-collapse',true).first();
5639        
5640         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5641             // it's collapsed or collapsing..
5642             return;
5643         }
5644         ce.removeClass('in'); // old...
5645         ce.setHeight(ce.getHeight());
5646         ce.removeClass('show');
5647         ce.addClass('collapsing');
5648         
5649         ce.on('transitionend', function() {
5650             ce.dom.style.height = '';
5651             ce.removeClass('collapsing');
5652             ce.addClass('collapse');
5653         }, this, { single: true} );
5654         ce.setHeight(0);
5655     }
5656     
5657     
5658     
5659 });
5660
5661
5662
5663  
5664
5665  /*
5666  * - LGPL
5667  *
5668  * navbar
5669  * 
5670  */
5671
5672 /**
5673  * @class Roo.bootstrap.NavSimplebar
5674  * @extends Roo.bootstrap.Navbar
5675  * Bootstrap Sidebar class
5676  *
5677  * @cfg {Boolean} inverse is inverted color
5678  * 
5679  * @cfg {String} type (nav | pills | tabs)
5680  * @cfg {Boolean} arrangement stacked | justified
5681  * @cfg {String} align (left | right) alignment
5682  * 
5683  * @cfg {Boolean} main (true|false) main nav bar? default false
5684  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5685  * 
5686  * @cfg {String} tag (header|footer|nav|div) default is nav 
5687
5688  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5689  * 
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSimplebar = function(config){
5698     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5702     
5703     inverse: false,
5704     
5705     type: false,
5706     arrangement: '',
5707     align : false,
5708     
5709     weight : 'light',
5710     
5711     main : false,
5712     
5713     
5714     tag : false,
5715     
5716     
5717     getAutoCreate : function(){
5718         
5719         
5720         var cfg = {
5721             tag : this.tag || 'div',
5722             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5723         };
5724         if (['light','white'].indexOf(this.weight) > -1) {
5725             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5726         }
5727         cfg.cls += ' bg-' + this.weight;
5728         
5729         if (this.inverse) {
5730             cfg.cls += ' navbar-inverse';
5731             
5732         }
5733         
5734         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5735         
5736         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5737             return cfg;
5738         }
5739         
5740         
5741     
5742         
5743         cfg.cn = [
5744             {
5745                 cls: 'nav nav-' + this.xtype,
5746                 tag : 'ul'
5747             }
5748         ];
5749         
5750          
5751         this.type = this.type || 'nav';
5752         if (['tabs','pills'].indexOf(this.type) != -1) {
5753             cfg.cn[0].cls += ' nav-' + this.type
5754         
5755         
5756         } else {
5757             if (this.type!=='nav') {
5758                 Roo.log('nav type must be nav/tabs/pills')
5759             }
5760             cfg.cn[0].cls += ' navbar-nav'
5761         }
5762         
5763         
5764         
5765         
5766         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.arrangement;
5768         }
5769         
5770         
5771         if (this.align === 'right') {
5772             cfg.cn[0].cls += ' navbar-right';
5773         }
5774         
5775         
5776         
5777         
5778         return cfg;
5779     
5780         
5781     }
5782     
5783     
5784     
5785 });
5786
5787
5788
5789  
5790
5791  
5792        /*
5793  * - LGPL
5794  *
5795  * navbar
5796  * navbar-fixed-top
5797  * navbar-expand-md  fixed-top 
5798  */
5799
5800 /**
5801  * @class Roo.bootstrap.NavHeaderbar
5802  * @extends Roo.bootstrap.NavSimplebar
5803  * Bootstrap Sidebar class
5804  *
5805  * @cfg {String} brand what is brand
5806  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5807  * @cfg {String} brand_href href of the brand
5808  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5809  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5810  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5811  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5812  * 
5813  * @constructor
5814  * Create a new Sidebar
5815  * @param {Object} config The config object
5816  */
5817
5818
5819 Roo.bootstrap.NavHeaderbar = function(config){
5820     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5821       
5822 };
5823
5824 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5825     
5826     position: '',
5827     brand: '',
5828     brand_href: false,
5829     srButton : true,
5830     autohide : false,
5831     desktopCenter : false,
5832    
5833     
5834     getAutoCreate : function(){
5835         
5836         var   cfg = {
5837             tag: this.nav || 'nav',
5838             cls: 'navbar navbar-expand-md',
5839             role: 'navigation',
5840             cn: []
5841         };
5842         
5843         var cn = cfg.cn;
5844         if (this.desktopCenter) {
5845             cn.push({cls : 'container', cn : []});
5846             cn = cn[0].cn;
5847         }
5848         
5849         if(this.srButton){
5850             var btn = {
5851                 tag: 'button',
5852                 type: 'button',
5853                 cls: 'navbar-toggle navbar-toggler',
5854                 'data-toggle': 'collapse',
5855                 cn: [
5856                     {
5857                         tag: 'span',
5858                         cls: 'sr-only',
5859                         html: 'Toggle navigation'
5860                     },
5861                     {
5862                         tag: 'span',
5863                         cls: 'icon-bar navbar-toggler-icon'
5864                     },
5865                     {
5866                         tag: 'span',
5867                         cls: 'icon-bar'
5868                     },
5869                     {
5870                         tag: 'span',
5871                         cls: 'icon-bar'
5872                     }
5873                 ]
5874             };
5875             
5876             cn.push( Roo.bootstrap.version == 4 ? btn : {
5877                 tag: 'div',
5878                 cls: 'navbar-header',
5879                 cn: [
5880                     btn
5881                 ]
5882             });
5883         }
5884         
5885         cn.push({
5886             tag: 'div',
5887             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5888             cn : []
5889         });
5890         
5891         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5892         
5893         if (['light','white'].indexOf(this.weight) > -1) {
5894             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5895         }
5896         cfg.cls += ' bg-' + this.weight;
5897         
5898         
5899         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5900             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5901             
5902             // tag can override this..
5903             
5904             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5905         }
5906         
5907         if (this.brand !== '') {
5908             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5909             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5910                 tag: 'a',
5911                 href: this.brand_href ? this.brand_href : '#',
5912                 cls: 'navbar-brand',
5913                 cn: [
5914                 this.brand
5915                 ]
5916             });
5917         }
5918         
5919         if(this.main){
5920             cfg.cls += ' main-nav';
5921         }
5922         
5923         
5924         return cfg;
5925
5926         
5927     },
5928     getHeaderChildContainer : function()
5929     {
5930         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5931             return this.el.select('.navbar-header',true).first();
5932         }
5933         
5934         return this.getChildContainer();
5935     },
5936     
5937     getChildContainer : function()
5938     {
5939          
5940         return this.el.select('.roo-navbar-collapse',true).first();
5941          
5942         
5943     },
5944     
5945     initEvents : function()
5946     {
5947         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5948         
5949         if (this.autohide) {
5950             
5951             var prevScroll = 0;
5952             var ft = this.el;
5953             
5954             Roo.get(document).on('scroll',function(e) {
5955                 var ns = Roo.get(document).getScroll().top;
5956                 var os = prevScroll;
5957                 prevScroll = ns;
5958                 
5959                 if(ns > os){
5960                     ft.removeClass('slideDown');
5961                     ft.addClass('slideUp');
5962                     return;
5963                 }
5964                 ft.removeClass('slideUp');
5965                 ft.addClass('slideDown');
5966                  
5967               
5968           },this);
5969         }
5970     }    
5971     
5972 });
5973
5974
5975
5976  
5977
5978  /*
5979  * - LGPL
5980  *
5981  * navbar
5982  * 
5983  */
5984
5985 /**
5986  * @class Roo.bootstrap.NavSidebar
5987  * @extends Roo.bootstrap.Navbar
5988  * Bootstrap Sidebar class
5989  * 
5990  * @constructor
5991  * Create a new Sidebar
5992  * @param {Object} config The config object
5993  */
5994
5995
5996 Roo.bootstrap.NavSidebar = function(config){
5997     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5998 };
5999
6000 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6001     
6002     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6003     
6004     getAutoCreate : function(){
6005         
6006         
6007         return  {
6008             tag: 'div',
6009             cls: 'sidebar sidebar-nav'
6010         };
6011     
6012         
6013     }
6014     
6015     
6016     
6017 });
6018
6019
6020
6021  
6022
6023  /*
6024  * - LGPL
6025  *
6026  * nav group
6027  * 
6028  */
6029
6030 /**
6031  * @class Roo.bootstrap.NavGroup
6032  * @extends Roo.bootstrap.Component
6033  * Bootstrap NavGroup class
6034  * @cfg {String} align (left|right)
6035  * @cfg {Boolean} inverse
6036  * @cfg {String} type (nav|pills|tab) default nav
6037  * @cfg {String} navId - reference Id for navbar.
6038  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6039  * 
6040  * @constructor
6041  * Create a new nav group
6042  * @param {Object} config The config object
6043  */
6044
6045 Roo.bootstrap.NavGroup = function(config){
6046     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6047     this.navItems = [];
6048    
6049     Roo.bootstrap.NavGroup.register(this);
6050      this.addEvents({
6051         /**
6052              * @event changed
6053              * Fires when the active item changes
6054              * @param {Roo.bootstrap.NavGroup} this
6055              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6056              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6057          */
6058         'changed': true
6059      });
6060     
6061 };
6062
6063 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6064     
6065     align: '',
6066     inverse: false,
6067     form: false,
6068     type: 'nav',
6069     navId : '',
6070     // private
6071     pilltype : true,
6072     
6073     navItems : false, 
6074     
6075     getAutoCreate : function()
6076     {
6077         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6078         
6079         cfg = {
6080             tag : 'ul',
6081             cls: 'nav' 
6082         };
6083         if (Roo.bootstrap.version == 4) {
6084             if (['tabs','pills'].indexOf(this.type) != -1) {
6085                 cfg.cls += ' nav-' + this.type; 
6086             } else {
6087                 // trying to remove so header bar can right align top?
6088                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6089                     // do not use on header bar... 
6090                     cfg.cls += ' navbar-nav';
6091                 }
6092             }
6093             
6094         } else {
6095             if (['tabs','pills'].indexOf(this.type) != -1) {
6096                 cfg.cls += ' nav-' + this.type
6097             } else {
6098                 if (this.type !== 'nav') {
6099                     Roo.log('nav type must be nav/tabs/pills')
6100                 }
6101                 cfg.cls += ' navbar-nav'
6102             }
6103         }
6104         
6105         if (this.parent() && this.parent().sidebar) {
6106             cfg = {
6107                 tag: 'ul',
6108                 cls: 'dashboard-menu sidebar-menu'
6109             };
6110             
6111             return cfg;
6112         }
6113         
6114         if (this.form === true) {
6115             cfg = {
6116                 tag: 'form',
6117                 cls: 'navbar-form form-inline'
6118             };
6119             //nav navbar-right ml-md-auto
6120             if (this.align === 'right') {
6121                 cfg.cls += ' navbar-right ml-md-auto';
6122             } else {
6123                 cfg.cls += ' navbar-left';
6124             }
6125         }
6126         
6127         if (this.align === 'right') {
6128             cfg.cls += ' navbar-right ml-md-auto';
6129         } else {
6130             cfg.cls += ' mr-auto';
6131         }
6132         
6133         if (this.inverse) {
6134             cfg.cls += ' navbar-inverse';
6135             
6136         }
6137         
6138         
6139         return cfg;
6140     },
6141     /**
6142     * sets the active Navigation item
6143     * @param {Roo.bootstrap.NavItem} the new current navitem
6144     */
6145     setActiveItem : function(item)
6146     {
6147         var prev = false;
6148         Roo.each(this.navItems, function(v){
6149             if (v == item) {
6150                 return ;
6151             }
6152             if (v.isActive()) {
6153                 v.setActive(false, true);
6154                 prev = v;
6155                 
6156             }
6157             
6158         });
6159
6160         item.setActive(true, true);
6161         this.fireEvent('changed', this, item, prev);
6162         
6163         
6164     },
6165     /**
6166     * gets the active Navigation item
6167     * @return {Roo.bootstrap.NavItem} the current navitem
6168     */
6169     getActive : function()
6170     {
6171         
6172         var prev = false;
6173         Roo.each(this.navItems, function(v){
6174             
6175             if (v.isActive()) {
6176                 prev = v;
6177                 
6178             }
6179             
6180         });
6181         return prev;
6182     },
6183     
6184     indexOfNav : function()
6185     {
6186         
6187         var prev = false;
6188         Roo.each(this.navItems, function(v,i){
6189             
6190             if (v.isActive()) {
6191                 prev = i;
6192                 
6193             }
6194             
6195         });
6196         return prev;
6197     },
6198     /**
6199     * adds a Navigation item
6200     * @param {Roo.bootstrap.NavItem} the navitem to add
6201     */
6202     addItem : function(cfg)
6203     {
6204         if (this.form && Roo.bootstrap.version == 4) {
6205             cfg.tag = 'div';
6206         }
6207         var cn = new Roo.bootstrap.NavItem(cfg);
6208         this.register(cn);
6209         cn.parentId = this.id;
6210         cn.onRender(this.el, null);
6211         return cn;
6212     },
6213     /**
6214     * register a Navigation item
6215     * @param {Roo.bootstrap.NavItem} the navitem to add
6216     */
6217     register : function(item)
6218     {
6219         this.navItems.push( item);
6220         item.navId = this.navId;
6221     
6222     },
6223     
6224     /**
6225     * clear all the Navigation item
6226     */
6227    
6228     clearAll : function()
6229     {
6230         this.navItems = [];
6231         this.el.dom.innerHTML = '';
6232     },
6233     
6234     getNavItem: function(tabId)
6235     {
6236         var ret = false;
6237         Roo.each(this.navItems, function(e) {
6238             if (e.tabId == tabId) {
6239                ret =  e;
6240                return false;
6241             }
6242             return true;
6243             
6244         });
6245         return ret;
6246     },
6247     
6248     setActiveNext : function()
6249     {
6250         var i = this.indexOfNav(this.getActive());
6251         if (i > this.navItems.length) {
6252             return;
6253         }
6254         this.setActiveItem(this.navItems[i+1]);
6255     },
6256     setActivePrev : function()
6257     {
6258         var i = this.indexOfNav(this.getActive());
6259         if (i  < 1) {
6260             return;
6261         }
6262         this.setActiveItem(this.navItems[i-1]);
6263     },
6264     clearWasActive : function(except) {
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId != except.tabId && e.was_active) {
6267                e.was_active = false;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273     },
6274     getWasActive : function ()
6275     {
6276         var r = false;
6277         Roo.each(this.navItems, function(e) {
6278             if (e.was_active) {
6279                r = e;
6280                return false;
6281             }
6282             return true;
6283             
6284         });
6285         return r;
6286     }
6287     
6288     
6289 });
6290
6291  
6292 Roo.apply(Roo.bootstrap.NavGroup, {
6293     
6294     groups: {},
6295      /**
6296     * register a Navigation Group
6297     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6298     */
6299     register : function(navgrp)
6300     {
6301         this.groups[navgrp.navId] = navgrp;
6302         
6303     },
6304     /**
6305     * fetch a Navigation Group based on the navigation ID
6306     * @param {string} the navgroup to add
6307     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6308     */
6309     get: function(navId) {
6310         if (typeof(this.groups[navId]) == 'undefined') {
6311             return false;
6312             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6313         }
6314         return this.groups[navId] ;
6315     }
6316     
6317     
6318     
6319 });
6320
6321  /*
6322  * - LGPL
6323  *
6324  * row
6325  * 
6326  */
6327
6328 /**
6329  * @class Roo.bootstrap.NavItem
6330  * @extends Roo.bootstrap.Component
6331  * Bootstrap Navbar.NavItem class
6332  * @cfg {String} href  link to
6333  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6334  * @cfg {Boolean} button_outline show and outlined button
6335  * @cfg {String} html content of button
6336  * @cfg {String} badge text inside badge
6337  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6338  * @cfg {String} glyphicon DEPRICATED - use fa
6339  * @cfg {String} icon DEPRICATED - use fa
6340  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6341  * @cfg {Boolean} active Is item active
6342  * @cfg {Boolean} disabled Is item disabled
6343  * @cfg {String} linkcls  Link Class
6344  * @cfg {Boolean} preventDefault (true | false) default false
6345  * @cfg {String} tabId the tab that this item activates.
6346  * @cfg {String} tagtype (a|span) render as a href or span?
6347  * @cfg {Boolean} animateRef (true|false) link to element default false  
6348   
6349  * @constructor
6350  * Create a new Navbar Item
6351  * @param {Object} config The config object
6352  */
6353 Roo.bootstrap.NavItem = function(config){
6354     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6355     this.addEvents({
6356         // raw events
6357         /**
6358          * @event click
6359          * The raw click event for the entire grid.
6360          * @param {Roo.EventObject} e
6361          */
6362         "click" : true,
6363          /**
6364             * @event changed
6365             * Fires when the active item active state changes
6366             * @param {Roo.bootstrap.NavItem} this
6367             * @param {boolean} state the new state
6368              
6369          */
6370         'changed': true,
6371         /**
6372             * @event scrollto
6373             * Fires when scroll to element
6374             * @param {Roo.bootstrap.NavItem} this
6375             * @param {Object} options
6376             * @param {Roo.EventObject} e
6377              
6378          */
6379         'scrollto': true
6380     });
6381    
6382 };
6383
6384 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6385     
6386     href: false,
6387     html: '',
6388     badge: '',
6389     icon: false,
6390     fa : false,
6391     glyphicon: false,
6392     active: false,
6393     preventDefault : false,
6394     tabId : false,
6395     tagtype : 'a',
6396     tag: 'li',
6397     disabled : false,
6398     animateRef : false,
6399     was_active : false,
6400     button_weight : '',
6401     button_outline : false,
6402     linkcls : '',
6403     navLink: false,
6404     
6405     getAutoCreate : function(){
6406          
6407         var cfg = {
6408             tag: this.tag,
6409             cls: 'nav-item'
6410         };
6411         
6412         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6413         
6414         if (this.active) {
6415             cfg.cls +=  ' active' ;
6416         }
6417         if (this.disabled) {
6418             cfg.cls += ' disabled';
6419         }
6420         
6421         // BS4 only?
6422         if (this.button_weight.length) {
6423             cfg.tag = this.href ? 'a' : 'button';
6424             cfg.html = this.html || '';
6425             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6426             if (this.href) {
6427                 cfg.href = this.href;
6428             }
6429             if (this.fa) {
6430                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6431             } else {
6432                 cfg.cls += " nav-html";
6433             }
6434             
6435             // menu .. should add dropdown-menu class - so no need for carat..
6436             
6437             if (this.badge !== '') {
6438                  
6439                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6440             }
6441             return cfg;
6442         }
6443         
6444         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6445             cfg.cn = [
6446                 {
6447                     tag: this.tagtype,
6448                     href : this.href || "#",
6449                     html: this.html || '',
6450                     cls : ''
6451                 }
6452             ];
6453             if (this.tagtype == 'a') {
6454                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6455         
6456             }
6457             if (this.icon) {
6458                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6459             } else  if (this.fa) {
6460                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6461             } else if(this.glyphicon) {
6462                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6463             } else {
6464                 cfg.cn[0].cls += " nav-html";
6465             }
6466             
6467             if (this.menu) {
6468                 cfg.cn[0].html += " <span class='caret'></span>";
6469              
6470             }
6471             
6472             if (this.badge !== '') {
6473                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475         }
6476         
6477         
6478         
6479         return cfg;
6480     },
6481     onRender : function(ct, position)
6482     {
6483        // Roo.log("Call onRender: " + this.xtype);
6484         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6485             this.tag = 'div';
6486         }
6487         
6488         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6489         this.navLink = this.el.select('.nav-link',true).first();
6490         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6491         return ret;
6492     },
6493       
6494     
6495     initEvents: function() 
6496     {
6497         if (typeof (this.menu) != 'undefined') {
6498             this.menu.parentType = this.xtype;
6499             this.menu.triggerEl = this.el;
6500             this.menu = this.addxtype(Roo.apply({}, this.menu));
6501         }
6502         
6503         this.el.on('click', this.onClick, this);
6504         
6505         //if(this.tagtype == 'span'){
6506         //    this.el.select('span',true).on('click', this.onClick, this);
6507         //}
6508        
6509         // at this point parent should be available..
6510         this.parent().register(this);
6511     },
6512     
6513     onClick : function(e)
6514     {
6515         if (e.getTarget('.dropdown-menu-item')) {
6516             // did you click on a menu itemm.... - then don't trigger onclick..
6517             return;
6518         }
6519         
6520         if(
6521                 this.preventDefault || 
6522                 this.href == '#' 
6523         ){
6524             Roo.log("NavItem - prevent Default?");
6525             e.preventDefault();
6526         }
6527         
6528         if (this.disabled) {
6529             return;
6530         }
6531         
6532         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6533         if (tg && tg.transition) {
6534             Roo.log("waiting for the transitionend");
6535             return;
6536         }
6537         
6538         
6539         
6540         //Roo.log("fire event clicked");
6541         if(this.fireEvent('click', this, e) === false){
6542             return;
6543         };
6544         
6545         if(this.tagtype == 'span'){
6546             return;
6547         }
6548         
6549         //Roo.log(this.href);
6550         var ael = this.el.select('a',true).first();
6551         //Roo.log(ael);
6552         
6553         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6554             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6555             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6556                 return; // ignore... - it's a 'hash' to another page.
6557             }
6558             Roo.log("NavItem - prevent Default?");
6559             e.preventDefault();
6560             this.scrollToElement(e);
6561         }
6562         
6563         
6564         var p =  this.parent();
6565    
6566         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6567             if (typeof(p.setActiveItem) !== 'undefined') {
6568                 p.setActiveItem(this);
6569             }
6570         }
6571         
6572         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6573         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6574             // remove the collapsed menu expand...
6575             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6576         }
6577     },
6578     
6579     isActive: function () {
6580         return this.active
6581     },
6582     setActive : function(state, fire, is_was_active)
6583     {
6584         if (this.active && !state && this.navId) {
6585             this.was_active = true;
6586             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6587             if (nv) {
6588                 nv.clearWasActive(this);
6589             }
6590             
6591         }
6592         this.active = state;
6593         
6594         if (!state ) {
6595             this.el.removeClass('active');
6596             this.navLink ? this.navLink.removeClass('active') : false;
6597         } else if (!this.el.hasClass('active')) {
6598             
6599             this.el.addClass('active');
6600             if (Roo.bootstrap.version == 4 && this.navLink ) {
6601                 this.navLink.addClass('active');
6602             }
6603             
6604         }
6605         if (fire) {
6606             this.fireEvent('changed', this, state);
6607         }
6608         
6609         // show a panel if it's registered and related..
6610         
6611         if (!this.navId || !this.tabId || !state || is_was_active) {
6612             return;
6613         }
6614         
6615         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6616         if (!tg) {
6617             return;
6618         }
6619         var pan = tg.getPanelByName(this.tabId);
6620         if (!pan) {
6621             return;
6622         }
6623         // if we can not flip to new panel - go back to old nav highlight..
6624         if (false == tg.showPanel(pan)) {
6625             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6626             if (nv) {
6627                 var onav = nv.getWasActive();
6628                 if (onav) {
6629                     onav.setActive(true, false, true);
6630                 }
6631             }
6632             
6633         }
6634         
6635         
6636         
6637     },
6638      // this should not be here...
6639     setDisabled : function(state)
6640     {
6641         this.disabled = state;
6642         if (!state ) {
6643             this.el.removeClass('disabled');
6644         } else if (!this.el.hasClass('disabled')) {
6645             this.el.addClass('disabled');
6646         }
6647         
6648     },
6649     
6650     /**
6651      * Fetch the element to display the tooltip on.
6652      * @return {Roo.Element} defaults to this.el
6653      */
6654     tooltipEl : function()
6655     {
6656         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6657     },
6658     
6659     scrollToElement : function(e)
6660     {
6661         var c = document.body;
6662         
6663         /*
6664          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6665          */
6666         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6667             c = document.documentElement;
6668         }
6669         
6670         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6671         
6672         if(!target){
6673             return;
6674         }
6675
6676         var o = target.calcOffsetsTo(c);
6677         
6678         var options = {
6679             target : target,
6680             value : o[1]
6681         };
6682         
6683         this.fireEvent('scrollto', this, options, e);
6684         
6685         Roo.get(c).scrollTo('top', options.value, true);
6686         
6687         return;
6688     },
6689     /**
6690      * Set the HTML (text content) of the item
6691      * @param {string} html  content for the nav item
6692      */
6693     setHtml : function(html)
6694     {
6695         this.html = html;
6696         this.htmlEl.dom.innerHTML = html;
6697         
6698     } 
6699 });
6700  
6701
6702  /*
6703  * - LGPL
6704  *
6705  * sidebar item
6706  *
6707  *  li
6708  *    <span> icon </span>
6709  *    <span> text </span>
6710  *    <span>badge </span>
6711  */
6712
6713 /**
6714  * @class Roo.bootstrap.NavSidebarItem
6715  * @extends Roo.bootstrap.NavItem
6716  * Bootstrap Navbar.NavSidebarItem class
6717  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6718  * {Boolean} open is the menu open
6719  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6720  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6721  * {String} buttonSize (sm|md|lg)the extra classes for the button
6722  * {Boolean} showArrow show arrow next to the text (default true)
6723  * @constructor
6724  * Create a new Navbar Button
6725  * @param {Object} config The config object
6726  */
6727 Roo.bootstrap.NavSidebarItem = function(config){
6728     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6729     this.addEvents({
6730         // raw events
6731         /**
6732          * @event click
6733          * The raw click event for the entire grid.
6734          * @param {Roo.EventObject} e
6735          */
6736         "click" : true,
6737          /**
6738             * @event changed
6739             * Fires when the active item active state changes
6740             * @param {Roo.bootstrap.NavSidebarItem} this
6741             * @param {boolean} state the new state
6742              
6743          */
6744         'changed': true
6745     });
6746    
6747 };
6748
6749 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6750     
6751     badgeWeight : 'default',
6752     
6753     open: false,
6754     
6755     buttonView : false,
6756     
6757     buttonWeight : 'default',
6758     
6759     buttonSize : 'md',
6760     
6761     showArrow : true,
6762     
6763     getAutoCreate : function(){
6764         
6765         
6766         var a = {
6767                 tag: 'a',
6768                 href : this.href || '#',
6769                 cls: '',
6770                 html : '',
6771                 cn : []
6772         };
6773         
6774         if(this.buttonView){
6775             a = {
6776                 tag: 'button',
6777                 href : this.href || '#',
6778                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6779                 html : this.html,
6780                 cn : []
6781             };
6782         }
6783         
6784         var cfg = {
6785             tag: 'li',
6786             cls: '',
6787             cn: [ a ]
6788         };
6789         
6790         if (this.active) {
6791             cfg.cls += ' active';
6792         }
6793         
6794         if (this.disabled) {
6795             cfg.cls += ' disabled';
6796         }
6797         if (this.open) {
6798             cfg.cls += ' open x-open';
6799         }
6800         // left icon..
6801         if (this.glyphicon || this.icon) {
6802             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6803             a.cn.push({ tag : 'i', cls : c }) ;
6804         }
6805         
6806         if(!this.buttonView){
6807             var span = {
6808                 tag: 'span',
6809                 html : this.html || ''
6810             };
6811
6812             a.cn.push(span);
6813             
6814         }
6815         
6816         if (this.badge !== '') {
6817             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6818         }
6819         
6820         if (this.menu) {
6821             
6822             if(this.showArrow){
6823                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6824             }
6825             
6826             a.cls += ' dropdown-toggle treeview' ;
6827         }
6828         
6829         return cfg;
6830     },
6831     
6832     initEvents : function()
6833     { 
6834         if (typeof (this.menu) != 'undefined') {
6835             this.menu.parentType = this.xtype;
6836             this.menu.triggerEl = this.el;
6837             this.menu = this.addxtype(Roo.apply({}, this.menu));
6838         }
6839         
6840         this.el.on('click', this.onClick, this);
6841         
6842         if(this.badge !== ''){
6843             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6844         }
6845         
6846     },
6847     
6848     onClick : function(e)
6849     {
6850         if(this.disabled){
6851             e.preventDefault();
6852             return;
6853         }
6854         
6855         if(this.preventDefault){
6856             e.preventDefault();
6857         }
6858         
6859         this.fireEvent('click', this, e);
6860     },
6861     
6862     disable : function()
6863     {
6864         this.setDisabled(true);
6865     },
6866     
6867     enable : function()
6868     {
6869         this.setDisabled(false);
6870     },
6871     
6872     setDisabled : function(state)
6873     {
6874         if(this.disabled == state){
6875             return;
6876         }
6877         
6878         this.disabled = state;
6879         
6880         if (state) {
6881             this.el.addClass('disabled');
6882             return;
6883         }
6884         
6885         this.el.removeClass('disabled');
6886         
6887         return;
6888     },
6889     
6890     setActive : function(state)
6891     {
6892         if(this.active == state){
6893             return;
6894         }
6895         
6896         this.active = state;
6897         
6898         if (state) {
6899             this.el.addClass('active');
6900             return;
6901         }
6902         
6903         this.el.removeClass('active');
6904         
6905         return;
6906     },
6907     
6908     isActive: function () 
6909     {
6910         return this.active;
6911     },
6912     
6913     setBadge : function(str)
6914     {
6915         if(!this.badgeEl){
6916             return;
6917         }
6918         
6919         this.badgeEl.dom.innerHTML = str;
6920     }
6921     
6922    
6923      
6924  
6925 });
6926  
6927
6928  /*
6929  * - LGPL
6930  *
6931  *  Breadcrumb Nav
6932  * 
6933  */
6934 Roo.namespace('Roo.bootstrap.breadcrumb');
6935
6936
6937 /**
6938  * @class Roo.bootstrap.breadcrumb.Nav
6939  * @extends Roo.bootstrap.Component
6940  * Bootstrap Breadcrumb Nav Class
6941  *  
6942  * @children Roo.bootstrap.breadcrumb.Item
6943  * 
6944  * @constructor
6945  * Create a new breadcrumb.Nav
6946  * @param {Object} config The config object
6947  */
6948
6949
6950 Roo.bootstrap.breadcrumb.Nav = function(config){
6951     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6952     
6953     
6954 };
6955
6956 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function()
6959     {
6960
6961         var cfg = {
6962             tag: 'nav',
6963             cn : [
6964                 {
6965                     tag : 'ol',
6966                     cls : 'breadcrumb'
6967                 }
6968             ]
6969             
6970         };
6971           
6972         return cfg;
6973     },
6974     
6975     initEvents: function()
6976     {
6977         this.olEl = this.el.select('ol',true).first();    
6978     },
6979     getChildContainer : function()
6980     {
6981         return this.olEl;  
6982     }
6983     
6984 });
6985
6986  /*
6987  * - LGPL
6988  *
6989  *  Breadcrumb Item
6990  * 
6991  */
6992
6993
6994 /**
6995  * @class Roo.bootstrap.breadcrumb.Nav
6996  * @extends Roo.bootstrap.Component
6997  * Bootstrap Breadcrumb Nav Class
6998  *  
6999  * @children Roo.bootstrap.breadcrumb.Component
7000  * @cfg {String} html the content of the link.
7001  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7002  * @cfg {Boolean} active is it active
7003
7004  * 
7005  * @constructor
7006  * Create a new breadcrumb.Nav
7007  * @param {Object} config The config object
7008  */
7009
7010 Roo.bootstrap.breadcrumb.Item = function(config){
7011     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7012     this.addEvents({
7013         // img events
7014         /**
7015          * @event click
7016          * The img click event for the img.
7017          * @param {Roo.EventObject} e
7018          */
7019         "click" : true
7020     });
7021     
7022 };
7023
7024 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7025     
7026     href: false,
7027     html : '',
7028     
7029     getAutoCreate : function()
7030     {
7031
7032         var cfg = {
7033             tag: 'li',
7034             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7035         };
7036         if (this.href !== false) {
7037             cfg.cn = [{
7038                 tag : 'a',
7039                 href : this.href,
7040                 html : this.html
7041             }];
7042         } else {
7043             cfg.html = this.html;
7044         }
7045         
7046         return cfg;
7047     },
7048     
7049     initEvents: function()
7050     {
7051         if (this.href) {
7052             this.el.select('a', true).first().on('click',this.onClick, this)
7053         }
7054         
7055     },
7056     onClick : function(e)
7057     {
7058         e.preventDefault();
7059         this.fireEvent('click',this,  e);
7060     }
7061     
7062 });
7063
7064  /*
7065  * - LGPL
7066  *
7067  * row
7068  * 
7069  */
7070
7071 /**
7072  * @class Roo.bootstrap.Row
7073  * @extends Roo.bootstrap.Component
7074  * Bootstrap Row class (contains columns...)
7075  * 
7076  * @constructor
7077  * Create a new Row
7078  * @param {Object} config The config object
7079  */
7080
7081 Roo.bootstrap.Row = function(config){
7082     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7083 };
7084
7085 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7086     
7087     getAutoCreate : function(){
7088        return {
7089             cls: 'row clearfix'
7090        };
7091     }
7092     
7093     
7094 });
7095
7096  
7097
7098  /*
7099  * - LGPL
7100  *
7101  * pagination
7102  * 
7103  */
7104
7105 /**
7106  * @class Roo.bootstrap.Pagination
7107  * @extends Roo.bootstrap.Component
7108  * Bootstrap Pagination class
7109  * @cfg {String} size xs | sm | md | lg
7110  * @cfg {Boolean} inverse false | true
7111  * 
7112  * @constructor
7113  * Create a new Pagination
7114  * @param {Object} config The config object
7115  */
7116
7117 Roo.bootstrap.Pagination = function(config){
7118     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7119 };
7120
7121 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7122     
7123     cls: false,
7124     size: false,
7125     inverse: false,
7126     
7127     getAutoCreate : function(){
7128         var cfg = {
7129             tag: 'ul',
7130                 cls: 'pagination'
7131         };
7132         if (this.inverse) {
7133             cfg.cls += ' inverse';
7134         }
7135         if (this.html) {
7136             cfg.html=this.html;
7137         }
7138         if (this.cls) {
7139             cfg.cls += " " + this.cls;
7140         }
7141         return cfg;
7142     }
7143    
7144 });
7145
7146  
7147
7148  /*
7149  * - LGPL
7150  *
7151  * Pagination item
7152  * 
7153  */
7154
7155
7156 /**
7157  * @class Roo.bootstrap.PaginationItem
7158  * @extends Roo.bootstrap.Component
7159  * Bootstrap PaginationItem class
7160  * @cfg {String} html text
7161  * @cfg {String} href the link
7162  * @cfg {Boolean} preventDefault (true | false) default true
7163  * @cfg {Boolean} active (true | false) default false
7164  * @cfg {Boolean} disabled default false
7165  * 
7166  * 
7167  * @constructor
7168  * Create a new PaginationItem
7169  * @param {Object} config The config object
7170  */
7171
7172
7173 Roo.bootstrap.PaginationItem = function(config){
7174     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7175     this.addEvents({
7176         // raw events
7177         /**
7178          * @event click
7179          * The raw click event for the entire grid.
7180          * @param {Roo.EventObject} e
7181          */
7182         "click" : true
7183     });
7184 };
7185
7186 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7187     
7188     href : false,
7189     html : false,
7190     preventDefault: true,
7191     active : false,
7192     cls : false,
7193     disabled: false,
7194     
7195     getAutoCreate : function(){
7196         var cfg= {
7197             tag: 'li',
7198             cn: [
7199                 {
7200                     tag : 'a',
7201                     href : this.href ? this.href : '#',
7202                     html : this.html ? this.html : ''
7203                 }
7204             ]
7205         };
7206         
7207         if(this.cls){
7208             cfg.cls = this.cls;
7209         }
7210         
7211         if(this.disabled){
7212             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7213         }
7214         
7215         if(this.active){
7216             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7217         }
7218         
7219         return cfg;
7220     },
7221     
7222     initEvents: function() {
7223         
7224         this.el.on('click', this.onClick, this);
7225         
7226     },
7227     onClick : function(e)
7228     {
7229         Roo.log('PaginationItem on click ');
7230         if(this.preventDefault){
7231             e.preventDefault();
7232         }
7233         
7234         if(this.disabled){
7235             return;
7236         }
7237         
7238         this.fireEvent('click', this, e);
7239     }
7240    
7241 });
7242
7243  
7244
7245  /*
7246  * - LGPL
7247  *
7248  * slider
7249  * 
7250  */
7251
7252
7253 /**
7254  * @class Roo.bootstrap.Slider
7255  * @extends Roo.bootstrap.Component
7256  * Bootstrap Slider class
7257  *    
7258  * @constructor
7259  * Create a new Slider
7260  * @param {Object} config The config object
7261  */
7262
7263 Roo.bootstrap.Slider = function(config){
7264     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7265 };
7266
7267 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7268     
7269     getAutoCreate : function(){
7270         
7271         var cfg = {
7272             tag: 'div',
7273             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7274             cn: [
7275                 {
7276                     tag: 'a',
7277                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7278                 }
7279             ]
7280         };
7281         
7282         return cfg;
7283     }
7284    
7285 });
7286
7287  /*
7288  * Based on:
7289  * Ext JS Library 1.1.1
7290  * Copyright(c) 2006-2007, Ext JS, LLC.
7291  *
7292  * Originally Released Under LGPL - original licence link has changed is not relivant.
7293  *
7294  * Fork - LGPL
7295  * <script type="text/javascript">
7296  */
7297  
7298
7299 /**
7300  * @class Roo.grid.ColumnModel
7301  * @extends Roo.util.Observable
7302  * This is the default implementation of a ColumnModel used by the Grid. It defines
7303  * the columns in the grid.
7304  * <br>Usage:<br>
7305  <pre><code>
7306  var colModel = new Roo.grid.ColumnModel([
7307         {header: "Ticker", width: 60, sortable: true, locked: true},
7308         {header: "Company Name", width: 150, sortable: true},
7309         {header: "Market Cap.", width: 100, sortable: true},
7310         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7311         {header: "Employees", width: 100, sortable: true, resizable: false}
7312  ]);
7313  </code></pre>
7314  * <p>
7315  
7316  * The config options listed for this class are options which may appear in each
7317  * individual column definition.
7318  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7319  * @constructor
7320  * @param {Object} config An Array of column config objects. See this class's
7321  * config objects for details.
7322 */
7323 Roo.grid.ColumnModel = function(config){
7324         /**
7325      * The config passed into the constructor
7326      */
7327     this.config = []; //config;
7328     this.lookup = {};
7329
7330     // if no id, create one
7331     // if the column does not have a dataIndex mapping,
7332     // map it to the order it is in the config
7333     for(var i = 0, len = config.length; i < len; i++){
7334         this.addColumn(config[i]);
7335         
7336     }
7337
7338     /**
7339      * The width of columns which have no width specified (defaults to 100)
7340      * @type Number
7341      */
7342     this.defaultWidth = 100;
7343
7344     /**
7345      * Default sortable of columns which have no sortable specified (defaults to false)
7346      * @type Boolean
7347      */
7348     this.defaultSortable = false;
7349
7350     this.addEvents({
7351         /**
7352              * @event widthchange
7353              * Fires when the width of a column changes.
7354              * @param {ColumnModel} this
7355              * @param {Number} columnIndex The column index
7356              * @param {Number} newWidth The new width
7357              */
7358             "widthchange": true,
7359         /**
7360              * @event headerchange
7361              * Fires when the text of a header changes.
7362              * @param {ColumnModel} this
7363              * @param {Number} columnIndex The column index
7364              * @param {Number} newText The new header text
7365              */
7366             "headerchange": true,
7367         /**
7368              * @event hiddenchange
7369              * Fires when a column is hidden or "unhidden".
7370              * @param {ColumnModel} this
7371              * @param {Number} columnIndex The column index
7372              * @param {Boolean} hidden true if hidden, false otherwise
7373              */
7374             "hiddenchange": true,
7375             /**
7376          * @event columnmoved
7377          * Fires when a column is moved.
7378          * @param {ColumnModel} this
7379          * @param {Number} oldIndex
7380          * @param {Number} newIndex
7381          */
7382         "columnmoved" : true,
7383         /**
7384          * @event columlockchange
7385          * Fires when a column's locked state is changed
7386          * @param {ColumnModel} this
7387          * @param {Number} colIndex
7388          * @param {Boolean} locked true if locked
7389          */
7390         "columnlockchange" : true
7391     });
7392     Roo.grid.ColumnModel.superclass.constructor.call(this);
7393 };
7394 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7395     /**
7396      * @cfg {String} header The header text to display in the Grid view.
7397      */
7398     /**
7399      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7400      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7401      * specified, the column's index is used as an index into the Record's data Array.
7402      */
7403     /**
7404      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7405      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7406      */
7407     /**
7408      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7409      * Defaults to the value of the {@link #defaultSortable} property.
7410      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7411      */
7412     /**
7413      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7414      */
7415     /**
7416      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7417      */
7418     /**
7419      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7420      */
7421     /**
7422      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7423      */
7424     /**
7425      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7426      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7427      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7428      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7429      */
7430        /**
7431      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7432      */
7433     /**
7434      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7435      */
7436     /**
7437      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7438      */
7439     /**
7440      * @cfg {String} cursor (Optional)
7441      */
7442     /**
7443      * @cfg {String} tooltip (Optional)
7444      */
7445     /**
7446      * @cfg {Number} xs (Optional)
7447      */
7448     /**
7449      * @cfg {Number} sm (Optional)
7450      */
7451     /**
7452      * @cfg {Number} md (Optional)
7453      */
7454     /**
7455      * @cfg {Number} lg (Optional)
7456      */
7457     /**
7458      * Returns the id of the column at the specified index.
7459      * @param {Number} index The column index
7460      * @return {String} the id
7461      */
7462     getColumnId : function(index){
7463         return this.config[index].id;
7464     },
7465
7466     /**
7467      * Returns the column for a specified id.
7468      * @param {String} id The column id
7469      * @return {Object} the column
7470      */
7471     getColumnById : function(id){
7472         return this.lookup[id];
7473     },
7474
7475     
7476     /**
7477      * Returns the column Object for a specified dataIndex.
7478      * @param {String} dataIndex The column dataIndex
7479      * @return {Object|Boolean} the column or false if not found
7480      */
7481     getColumnByDataIndex: function(dataIndex){
7482         var index = this.findColumnIndex(dataIndex);
7483         return index > -1 ? this.config[index] : false;
7484     },
7485     
7486     /**
7487      * Returns the index for a specified column id.
7488      * @param {String} id The column id
7489      * @return {Number} the index, or -1 if not found
7490      */
7491     getIndexById : function(id){
7492         for(var i = 0, len = this.config.length; i < len; i++){
7493             if(this.config[i].id == id){
7494                 return i;
7495             }
7496         }
7497         return -1;
7498     },
7499     
7500     /**
7501      * Returns the index for a specified column dataIndex.
7502      * @param {String} dataIndex The column dataIndex
7503      * @return {Number} the index, or -1 if not found
7504      */
7505     
7506     findColumnIndex : function(dataIndex){
7507         for(var i = 0, len = this.config.length; i < len; i++){
7508             if(this.config[i].dataIndex == dataIndex){
7509                 return i;
7510             }
7511         }
7512         return -1;
7513     },
7514     
7515     
7516     moveColumn : function(oldIndex, newIndex){
7517         var c = this.config[oldIndex];
7518         this.config.splice(oldIndex, 1);
7519         this.config.splice(newIndex, 0, c);
7520         this.dataMap = null;
7521         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7522     },
7523
7524     isLocked : function(colIndex){
7525         return this.config[colIndex].locked === true;
7526     },
7527
7528     setLocked : function(colIndex, value, suppressEvent){
7529         if(this.isLocked(colIndex) == value){
7530             return;
7531         }
7532         this.config[colIndex].locked = value;
7533         if(!suppressEvent){
7534             this.fireEvent("columnlockchange", this, colIndex, value);
7535         }
7536     },
7537
7538     getTotalLockedWidth : function(){
7539         var totalWidth = 0;
7540         for(var i = 0; i < this.config.length; i++){
7541             if(this.isLocked(i) && !this.isHidden(i)){
7542                 this.totalWidth += this.getColumnWidth(i);
7543             }
7544         }
7545         return totalWidth;
7546     },
7547
7548     getLockedCount : function(){
7549         for(var i = 0, len = this.config.length; i < len; i++){
7550             if(!this.isLocked(i)){
7551                 return i;
7552             }
7553         }
7554         
7555         return this.config.length;
7556     },
7557
7558     /**
7559      * Returns the number of columns.
7560      * @return {Number}
7561      */
7562     getColumnCount : function(visibleOnly){
7563         if(visibleOnly === true){
7564             var c = 0;
7565             for(var i = 0, len = this.config.length; i < len; i++){
7566                 if(!this.isHidden(i)){
7567                     c++;
7568                 }
7569             }
7570             return c;
7571         }
7572         return this.config.length;
7573     },
7574
7575     /**
7576      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7577      * @param {Function} fn
7578      * @param {Object} scope (optional)
7579      * @return {Array} result
7580      */
7581     getColumnsBy : function(fn, scope){
7582         var r = [];
7583         for(var i = 0, len = this.config.length; i < len; i++){
7584             var c = this.config[i];
7585             if(fn.call(scope||this, c, i) === true){
7586                 r[r.length] = c;
7587             }
7588         }
7589         return r;
7590     },
7591
7592     /**
7593      * Returns true if the specified column is sortable.
7594      * @param {Number} col The column index
7595      * @return {Boolean}
7596      */
7597     isSortable : function(col){
7598         if(typeof this.config[col].sortable == "undefined"){
7599             return this.defaultSortable;
7600         }
7601         return this.config[col].sortable;
7602     },
7603
7604     /**
7605      * Returns the rendering (formatting) function defined for the column.
7606      * @param {Number} col The column index.
7607      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7608      */
7609     getRenderer : function(col){
7610         if(!this.config[col].renderer){
7611             return Roo.grid.ColumnModel.defaultRenderer;
7612         }
7613         return this.config[col].renderer;
7614     },
7615
7616     /**
7617      * Sets the rendering (formatting) function for a column.
7618      * @param {Number} col The column index
7619      * @param {Function} fn The function to use to process the cell's raw data
7620      * to return HTML markup for the grid view. The render function is called with
7621      * the following parameters:<ul>
7622      * <li>Data value.</li>
7623      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7624      * <li>css A CSS style string to apply to the table cell.</li>
7625      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7626      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7627      * <li>Row index</li>
7628      * <li>Column index</li>
7629      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7630      */
7631     setRenderer : function(col, fn){
7632         this.config[col].renderer = fn;
7633     },
7634
7635     /**
7636      * Returns the width for the specified column.
7637      * @param {Number} col The column index
7638      * @return {Number}
7639      */
7640     getColumnWidth : function(col){
7641         return this.config[col].width * 1 || this.defaultWidth;
7642     },
7643
7644     /**
7645      * Sets the width for a column.
7646      * @param {Number} col The column index
7647      * @param {Number} width The new width
7648      */
7649     setColumnWidth : function(col, width, suppressEvent){
7650         this.config[col].width = width;
7651         this.totalWidth = null;
7652         if(!suppressEvent){
7653              this.fireEvent("widthchange", this, col, width);
7654         }
7655     },
7656
7657     /**
7658      * Returns the total width of all columns.
7659      * @param {Boolean} includeHidden True to include hidden column widths
7660      * @return {Number}
7661      */
7662     getTotalWidth : function(includeHidden){
7663         if(!this.totalWidth){
7664             this.totalWidth = 0;
7665             for(var i = 0, len = this.config.length; i < len; i++){
7666                 if(includeHidden || !this.isHidden(i)){
7667                     this.totalWidth += this.getColumnWidth(i);
7668                 }
7669             }
7670         }
7671         return this.totalWidth;
7672     },
7673
7674     /**
7675      * Returns the header for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnHeader : function(col){
7680         return this.config[col].header;
7681     },
7682
7683     /**
7684      * Sets the header for a column.
7685      * @param {Number} col The column index
7686      * @param {String} header The new header
7687      */
7688     setColumnHeader : function(col, header){
7689         this.config[col].header = header;
7690         this.fireEvent("headerchange", this, col, header);
7691     },
7692
7693     /**
7694      * Returns the tooltip for the specified column.
7695      * @param {Number} col The column index
7696      * @return {String}
7697      */
7698     getColumnTooltip : function(col){
7699             return this.config[col].tooltip;
7700     },
7701     /**
7702      * Sets the tooltip for a column.
7703      * @param {Number} col The column index
7704      * @param {String} tooltip The new tooltip
7705      */
7706     setColumnTooltip : function(col, tooltip){
7707             this.config[col].tooltip = tooltip;
7708     },
7709
7710     /**
7711      * Returns the dataIndex for the specified column.
7712      * @param {Number} col The column index
7713      * @return {Number}
7714      */
7715     getDataIndex : function(col){
7716         return this.config[col].dataIndex;
7717     },
7718
7719     /**
7720      * Sets the dataIndex for a column.
7721      * @param {Number} col The column index
7722      * @param {Number} dataIndex The new dataIndex
7723      */
7724     setDataIndex : function(col, dataIndex){
7725         this.config[col].dataIndex = dataIndex;
7726     },
7727
7728     
7729     
7730     /**
7731      * Returns true if the cell is editable.
7732      * @param {Number} colIndex The column index
7733      * @param {Number} rowIndex The row index - this is nto actually used..?
7734      * @return {Boolean}
7735      */
7736     isCellEditable : function(colIndex, rowIndex){
7737         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7738     },
7739
7740     /**
7741      * Returns the editor defined for the cell/column.
7742      * return false or null to disable editing.
7743      * @param {Number} colIndex The column index
7744      * @param {Number} rowIndex The row index
7745      * @return {Object}
7746      */
7747     getCellEditor : function(colIndex, rowIndex){
7748         return this.config[colIndex].editor;
7749     },
7750
7751     /**
7752      * Sets if a column is editable.
7753      * @param {Number} col The column index
7754      * @param {Boolean} editable True if the column is editable
7755      */
7756     setEditable : function(col, editable){
7757         this.config[col].editable = editable;
7758     },
7759
7760
7761     /**
7762      * Returns true if the column is hidden.
7763      * @param {Number} colIndex The column index
7764      * @return {Boolean}
7765      */
7766     isHidden : function(colIndex){
7767         return this.config[colIndex].hidden;
7768     },
7769
7770
7771     /**
7772      * Returns true if the column width cannot be changed
7773      */
7774     isFixed : function(colIndex){
7775         return this.config[colIndex].fixed;
7776     },
7777
7778     /**
7779      * Returns true if the column can be resized
7780      * @return {Boolean}
7781      */
7782     isResizable : function(colIndex){
7783         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7784     },
7785     /**
7786      * Sets if a column is hidden.
7787      * @param {Number} colIndex The column index
7788      * @param {Boolean} hidden True if the column is hidden
7789      */
7790     setHidden : function(colIndex, hidden){
7791         this.config[colIndex].hidden = hidden;
7792         this.totalWidth = null;
7793         this.fireEvent("hiddenchange", this, colIndex, hidden);
7794     },
7795
7796     /**
7797      * Sets the editor for a column.
7798      * @param {Number} col The column index
7799      * @param {Object} editor The editor object
7800      */
7801     setEditor : function(col, editor){
7802         this.config[col].editor = editor;
7803     },
7804     /**
7805      * Add a column (experimental...) - defaults to adding to the end..
7806      * @param {Object} config 
7807     */
7808     addColumn : function(c)
7809     {
7810     
7811         var i = this.config.length;
7812         this.config[i] = c;
7813         
7814         if(typeof c.dataIndex == "undefined"){
7815             c.dataIndex = i;
7816         }
7817         if(typeof c.renderer == "string"){
7818             c.renderer = Roo.util.Format[c.renderer];
7819         }
7820         if(typeof c.id == "undefined"){
7821             c.id = Roo.id();
7822         }
7823         if(c.editor && c.editor.xtype){
7824             c.editor  = Roo.factory(c.editor, Roo.grid);
7825         }
7826         if(c.editor && c.editor.isFormField){
7827             c.editor = new Roo.grid.GridEditor(c.editor);
7828         }
7829         this.lookup[c.id] = c;
7830     }
7831     
7832 });
7833
7834 Roo.grid.ColumnModel.defaultRenderer = function(value)
7835 {
7836     if(typeof value == "object") {
7837         return value;
7838     }
7839         if(typeof value == "string" && value.length < 1){
7840             return "&#160;";
7841         }
7842     
7843         return String.format("{0}", value);
7844 };
7845
7846 // Alias for backwards compatibility
7847 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7848 /*
7849  * Based on:
7850  * Ext JS Library 1.1.1
7851  * Copyright(c) 2006-2007, Ext JS, LLC.
7852  *
7853  * Originally Released Under LGPL - original licence link has changed is not relivant.
7854  *
7855  * Fork - LGPL
7856  * <script type="text/javascript">
7857  */
7858  
7859 /**
7860  * @class Roo.LoadMask
7861  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7862  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7863  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7864  * element's UpdateManager load indicator and will be destroyed after the initial load.
7865  * @constructor
7866  * Create a new LoadMask
7867  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7868  * @param {Object} config The config object
7869  */
7870 Roo.LoadMask = function(el, config){
7871     this.el = Roo.get(el);
7872     Roo.apply(this, config);
7873     if(this.store){
7874         this.store.on('beforeload', this.onBeforeLoad, this);
7875         this.store.on('load', this.onLoad, this);
7876         this.store.on('loadexception', this.onLoadException, this);
7877         this.removeMask = false;
7878     }else{
7879         var um = this.el.getUpdateManager();
7880         um.showLoadIndicator = false; // disable the default indicator
7881         um.on('beforeupdate', this.onBeforeLoad, this);
7882         um.on('update', this.onLoad, this);
7883         um.on('failure', this.onLoad, this);
7884         this.removeMask = true;
7885     }
7886 };
7887
7888 Roo.LoadMask.prototype = {
7889     /**
7890      * @cfg {Boolean} removeMask
7891      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7892      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7893      */
7894     /**
7895      * @cfg {String} msg
7896      * The text to display in a centered loading message box (defaults to 'Loading...')
7897      */
7898     msg : 'Loading...',
7899     /**
7900      * @cfg {String} msgCls
7901      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7902      */
7903     msgCls : 'x-mask-loading',
7904
7905     /**
7906      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7907      * @type Boolean
7908      */
7909     disabled: false,
7910
7911     /**
7912      * Disables the mask to prevent it from being displayed
7913      */
7914     disable : function(){
7915        this.disabled = true;
7916     },
7917
7918     /**
7919      * Enables the mask so that it can be displayed
7920      */
7921     enable : function(){
7922         this.disabled = false;
7923     },
7924     
7925     onLoadException : function()
7926     {
7927         Roo.log(arguments);
7928         
7929         if (typeof(arguments[3]) != 'undefined') {
7930             Roo.MessageBox.alert("Error loading",arguments[3]);
7931         } 
7932         /*
7933         try {
7934             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7935                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7936             }   
7937         } catch(e) {
7938             
7939         }
7940         */
7941     
7942         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7943     },
7944     // private
7945     onLoad : function()
7946     {
7947         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7948     },
7949
7950     // private
7951     onBeforeLoad : function(){
7952         if(!this.disabled){
7953             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7954         }
7955     },
7956
7957     // private
7958     destroy : function(){
7959         if(this.store){
7960             this.store.un('beforeload', this.onBeforeLoad, this);
7961             this.store.un('load', this.onLoad, this);
7962             this.store.un('loadexception', this.onLoadException, this);
7963         }else{
7964             var um = this.el.getUpdateManager();
7965             um.un('beforeupdate', this.onBeforeLoad, this);
7966             um.un('update', this.onLoad, this);
7967             um.un('failure', this.onLoad, this);
7968         }
7969     }
7970 };/*
7971  * - LGPL
7972  *
7973  * table
7974  * 
7975  */
7976
7977 /**
7978  * @class Roo.bootstrap.Table
7979  * @extends Roo.bootstrap.Component
7980  * Bootstrap Table class
7981  * @cfg {String} cls table class
7982  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7983  * @cfg {String} bgcolor Specifies the background color for a table
7984  * @cfg {Number} border Specifies whether the table cells should have borders or not
7985  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7986  * @cfg {Number} cellspacing Specifies the space between cells
7987  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7988  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7989  * @cfg {String} sortable Specifies that the table should be sortable
7990  * @cfg {String} summary Specifies a summary of the content of a table
7991  * @cfg {Number} width Specifies the width of a table
7992  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7993  * 
7994  * @cfg {boolean} striped Should the rows be alternative striped
7995  * @cfg {boolean} bordered Add borders to the table
7996  * @cfg {boolean} hover Add hover highlighting
7997  * @cfg {boolean} condensed Format condensed
7998  * @cfg {boolean} responsive Format condensed
7999  * @cfg {Boolean} loadMask (true|false) default false
8000  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8001  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8002  * @cfg {Boolean} rowSelection (true|false) default false
8003  * @cfg {Boolean} cellSelection (true|false) default false
8004  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8005  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8006  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8007  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8008  
8009  * 
8010  * @constructor
8011  * Create a new Table
8012  * @param {Object} config The config object
8013  */
8014
8015 Roo.bootstrap.Table = function(config){
8016     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8017     
8018   
8019     
8020     // BC...
8021     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8022     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8023     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8024     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8025     
8026     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8027     if (this.sm) {
8028         this.sm.grid = this;
8029         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8030         this.sm = this.selModel;
8031         this.sm.xmodule = this.xmodule || false;
8032     }
8033     
8034     if (this.cm && typeof(this.cm.config) == 'undefined') {
8035         this.colModel = new Roo.grid.ColumnModel(this.cm);
8036         this.cm = this.colModel;
8037         this.cm.xmodule = this.xmodule || false;
8038     }
8039     if (this.store) {
8040         this.store= Roo.factory(this.store, Roo.data);
8041         this.ds = this.store;
8042         this.ds.xmodule = this.xmodule || false;
8043          
8044     }
8045     if (this.footer && this.store) {
8046         this.footer.dataSource = this.ds;
8047         this.footer = Roo.factory(this.footer);
8048     }
8049     
8050     /** @private */
8051     this.addEvents({
8052         /**
8053          * @event cellclick
8054          * Fires when a cell is clicked
8055          * @param {Roo.bootstrap.Table} this
8056          * @param {Roo.Element} el
8057          * @param {Number} rowIndex
8058          * @param {Number} columnIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "cellclick" : true,
8062         /**
8063          * @event celldblclick
8064          * Fires when a cell is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Number} columnIndex
8069          * @param {Roo.EventObject} e
8070          */
8071         "celldblclick" : true,
8072         /**
8073          * @event rowclick
8074          * Fires when a row is clicked
8075          * @param {Roo.bootstrap.Table} this
8076          * @param {Roo.Element} el
8077          * @param {Number} rowIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "rowclick" : true,
8081         /**
8082          * @event rowdblclick
8083          * Fires when a row is double clicked
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Roo.EventObject} e
8088          */
8089         "rowdblclick" : true,
8090         /**
8091          * @event mouseover
8092          * Fires when a mouseover occur
8093          * @param {Roo.bootstrap.Table} this
8094          * @param {Roo.Element} el
8095          * @param {Number} rowIndex
8096          * @param {Number} columnIndex
8097          * @param {Roo.EventObject} e
8098          */
8099         "mouseover" : true,
8100         /**
8101          * @event mouseout
8102          * Fires when a mouseout occur
8103          * @param {Roo.bootstrap.Table} this
8104          * @param {Roo.Element} el
8105          * @param {Number} rowIndex
8106          * @param {Number} columnIndex
8107          * @param {Roo.EventObject} e
8108          */
8109         "mouseout" : true,
8110         /**
8111          * @event rowclass
8112          * Fires when a row is rendered, so you can change add a style to it.
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8115          */
8116         'rowclass' : true,
8117           /**
8118          * @event rowsrendered
8119          * Fires when all the  rows have been rendered
8120          * @param {Roo.bootstrap.Table} this
8121          */
8122         'rowsrendered' : true,
8123         /**
8124          * @event contextmenu
8125          * The raw contextmenu event for the entire grid.
8126          * @param {Roo.EventObject} e
8127          */
8128         "contextmenu" : true,
8129         /**
8130          * @event rowcontextmenu
8131          * Fires when a row is right clicked
8132          * @param {Roo.bootstrap.Table} this
8133          * @param {Number} rowIndex
8134          * @param {Roo.EventObject} e
8135          */
8136         "rowcontextmenu" : true,
8137         /**
8138          * @event cellcontextmenu
8139          * Fires when a cell is right clicked
8140          * @param {Roo.bootstrap.Table} this
8141          * @param {Number} rowIndex
8142          * @param {Number} cellIndex
8143          * @param {Roo.EventObject} e
8144          */
8145          "cellcontextmenu" : true,
8146          /**
8147          * @event headercontextmenu
8148          * Fires when a header is right clicked
8149          * @param {Roo.bootstrap.Table} this
8150          * @param {Number} columnIndex
8151          * @param {Roo.EventObject} e
8152          */
8153         "headercontextmenu" : true
8154     });
8155 };
8156
8157 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8158     
8159     cls: false,
8160     align: false,
8161     bgcolor: false,
8162     border: false,
8163     cellpadding: false,
8164     cellspacing: false,
8165     frame: false,
8166     rules: false,
8167     sortable: false,
8168     summary: false,
8169     width: false,
8170     striped : false,
8171     scrollBody : false,
8172     bordered: false,
8173     hover:  false,
8174     condensed : false,
8175     responsive : false,
8176     sm : false,
8177     cm : false,
8178     store : false,
8179     loadMask : false,
8180     footerShow : true,
8181     headerShow : true,
8182   
8183     rowSelection : false,
8184     cellSelection : false,
8185     layout : false,
8186     
8187     // Roo.Element - the tbody
8188     mainBody: false,
8189     // Roo.Element - thead element
8190     mainHead: false,
8191     
8192     container: false, // used by gridpanel...
8193     
8194     lazyLoad : false,
8195     
8196     CSS : Roo.util.CSS,
8197     
8198     auto_hide_footer : false,
8199     
8200     getAutoCreate : function()
8201     {
8202         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8203         
8204         cfg = {
8205             tag: 'table',
8206             cls : 'table',
8207             cn : []
8208         };
8209         if (this.scrollBody) {
8210             cfg.cls += ' table-body-fixed';
8211         }    
8212         if (this.striped) {
8213             cfg.cls += ' table-striped';
8214         }
8215         
8216         if (this.hover) {
8217             cfg.cls += ' table-hover';
8218         }
8219         if (this.bordered) {
8220             cfg.cls += ' table-bordered';
8221         }
8222         if (this.condensed) {
8223             cfg.cls += ' table-condensed';
8224         }
8225         if (this.responsive) {
8226             cfg.cls += ' table-responsive';
8227         }
8228         
8229         if (this.cls) {
8230             cfg.cls+=  ' ' +this.cls;
8231         }
8232         
8233         // this lot should be simplifed...
8234         var _t = this;
8235         var cp = [
8236             'align',
8237             'bgcolor',
8238             'border',
8239             'cellpadding',
8240             'cellspacing',
8241             'frame',
8242             'rules',
8243             'sortable',
8244             'summary',
8245             'width'
8246         ].forEach(function(k) {
8247             if (_t[k]) {
8248                 cfg[k] = _t[k];
8249             }
8250         });
8251         
8252         
8253         if (this.layout) {
8254             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8255         }
8256         
8257         if(this.store || this.cm){
8258             if(this.headerShow){
8259                 cfg.cn.push(this.renderHeader());
8260             }
8261             
8262             cfg.cn.push(this.renderBody());
8263             
8264             if(this.footerShow){
8265                 cfg.cn.push(this.renderFooter());
8266             }
8267             // where does this come from?
8268             //cfg.cls+=  ' TableGrid';
8269         }
8270         
8271         return { cn : [ cfg ] };
8272     },
8273     
8274     initEvents : function()
8275     {   
8276         if(!this.store || !this.cm){
8277             return;
8278         }
8279         if (this.selModel) {
8280             this.selModel.initEvents();
8281         }
8282         
8283         
8284         //Roo.log('initEvents with ds!!!!');
8285         
8286         this.mainBody = this.el.select('tbody', true).first();
8287         this.mainHead = this.el.select('thead', true).first();
8288         this.mainFoot = this.el.select('tfoot', true).first();
8289         
8290         
8291         
8292         
8293         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8294             e.on('click', this.sort, this);
8295         }, this);
8296         
8297         this.mainBody.on("click", this.onClick, this);
8298         this.mainBody.on("dblclick", this.onDblClick, this);
8299         
8300         // why is this done????? = it breaks dialogs??
8301         //this.parent().el.setStyle('position', 'relative');
8302         
8303         
8304         if (this.footer) {
8305             this.footer.parentId = this.id;
8306             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8307             
8308             if(this.lazyLoad){
8309                 this.el.select('tfoot tr td').first().addClass('hide');
8310             }
8311         } 
8312         
8313         if(this.loadMask) {
8314             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8315         }
8316         
8317         this.store.on('load', this.onLoad, this);
8318         this.store.on('beforeload', this.onBeforeLoad, this);
8319         this.store.on('update', this.onUpdate, this);
8320         this.store.on('add', this.onAdd, this);
8321         this.store.on("clear", this.clear, this);
8322         
8323         this.el.on("contextmenu", this.onContextMenu, this);
8324         
8325         this.mainBody.on('scroll', this.onBodyScroll, this);
8326         
8327         this.cm.on("headerchange", this.onHeaderChange, this);
8328         
8329         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8330         
8331     },
8332     
8333     onContextMenu : function(e, t)
8334     {
8335         this.processEvent("contextmenu", e);
8336     },
8337     
8338     processEvent : function(name, e)
8339     {
8340         if (name != 'touchstart' ) {
8341             this.fireEvent(name, e);    
8342         }
8343         
8344         var t = e.getTarget();
8345         
8346         var cell = Roo.get(t);
8347         
8348         if(!cell){
8349             return;
8350         }
8351         
8352         if(cell.findParent('tfoot', false, true)){
8353             return;
8354         }
8355         
8356         if(cell.findParent('thead', false, true)){
8357             
8358             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8359                 cell = Roo.get(t).findParent('th', false, true);
8360                 if (!cell) {
8361                     Roo.log("failed to find th in thead?");
8362                     Roo.log(e.getTarget());
8363                     return;
8364                 }
8365             }
8366             
8367             var cellIndex = cell.dom.cellIndex;
8368             
8369             var ename = name == 'touchstart' ? 'click' : name;
8370             this.fireEvent("header" + ename, this, cellIndex, e);
8371             
8372             return;
8373         }
8374         
8375         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8376             cell = Roo.get(t).findParent('td', false, true);
8377             if (!cell) {
8378                 Roo.log("failed to find th in tbody?");
8379                 Roo.log(e.getTarget());
8380                 return;
8381             }
8382         }
8383         
8384         var row = cell.findParent('tr', false, true);
8385         var cellIndex = cell.dom.cellIndex;
8386         var rowIndex = row.dom.rowIndex - 1;
8387         
8388         if(row !== false){
8389             
8390             this.fireEvent("row" + name, this, rowIndex, e);
8391             
8392             if(cell !== false){
8393             
8394                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8395             }
8396         }
8397         
8398     },
8399     
8400     onMouseover : function(e, el)
8401     {
8402         var cell = Roo.get(el);
8403         
8404         if(!cell){
8405             return;
8406         }
8407         
8408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409             cell = cell.findParent('td', false, true);
8410         }
8411         
8412         var row = cell.findParent('tr', false, true);
8413         var cellIndex = cell.dom.cellIndex;
8414         var rowIndex = row.dom.rowIndex - 1; // start from 0
8415         
8416         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8417         
8418     },
8419     
8420     onMouseout : function(e, el)
8421     {
8422         var cell = Roo.get(el);
8423         
8424         if(!cell){
8425             return;
8426         }
8427         
8428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8429             cell = cell.findParent('td', false, true);
8430         }
8431         
8432         var row = cell.findParent('tr', false, true);
8433         var cellIndex = cell.dom.cellIndex;
8434         var rowIndex = row.dom.rowIndex - 1; // start from 0
8435         
8436         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8437         
8438     },
8439     
8440     onClick : function(e, el)
8441     {
8442         var cell = Roo.get(el);
8443         
8444         if(!cell || (!this.cellSelection && !this.rowSelection)){
8445             return;
8446         }
8447         
8448         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8449             cell = cell.findParent('td', false, true);
8450         }
8451         
8452         if(!cell || typeof(cell) == 'undefined'){
8453             return;
8454         }
8455         
8456         var row = cell.findParent('tr', false, true);
8457         
8458         if(!row || typeof(row) == 'undefined'){
8459             return;
8460         }
8461         
8462         var cellIndex = cell.dom.cellIndex;
8463         var rowIndex = this.getRowIndex(row);
8464         
8465         // why??? - should these not be based on SelectionModel?
8466         //if(this.cellSelection){
8467             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8468         //}
8469         
8470         //if(this.rowSelection){
8471             this.fireEvent('rowclick', this, row, rowIndex, e);
8472         //}
8473          
8474     },
8475         
8476     onDblClick : function(e,el)
8477     {
8478         var cell = Roo.get(el);
8479         
8480         if(!cell || (!this.cellSelection && !this.rowSelection)){
8481             return;
8482         }
8483         
8484         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8485             cell = cell.findParent('td', false, true);
8486         }
8487         
8488         if(!cell || typeof(cell) == 'undefined'){
8489             return;
8490         }
8491         
8492         var row = cell.findParent('tr', false, true);
8493         
8494         if(!row || typeof(row) == 'undefined'){
8495             return;
8496         }
8497         
8498         var cellIndex = cell.dom.cellIndex;
8499         var rowIndex = this.getRowIndex(row);
8500         
8501         if(this.cellSelection){
8502             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8503         }
8504         
8505         if(this.rowSelection){
8506             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8507         }
8508     },
8509     
8510     sort : function(e,el)
8511     {
8512         var col = Roo.get(el);
8513         
8514         if(!col.hasClass('sortable')){
8515             return;
8516         }
8517         
8518         var sort = col.attr('sort');
8519         var dir = 'ASC';
8520         
8521         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8522             dir = 'DESC';
8523         }
8524         
8525         this.store.sortInfo = {field : sort, direction : dir};
8526         
8527         if (this.footer) {
8528             Roo.log("calling footer first");
8529             this.footer.onClick('first');
8530         } else {
8531         
8532             this.store.load({ params : { start : 0 } });
8533         }
8534     },
8535     
8536     renderHeader : function()
8537     {
8538         var header = {
8539             tag: 'thead',
8540             cn : []
8541         };
8542         
8543         var cm = this.cm;
8544         this.totalWidth = 0;
8545         
8546         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8547             
8548             var config = cm.config[i];
8549             
8550             var c = {
8551                 tag: 'th',
8552                 cls : 'x-hcol-' + i,
8553                 style : '',
8554                 
8555                 html: cm.getColumnHeader(i)
8556             };
8557             
8558             var tooltip = cm.getColumnTooltip(i);
8559             if (tooltip) {
8560                 c.tooltip = tooltip;
8561             }
8562             
8563             
8564             var hh = '';
8565             
8566             if(typeof(config.sortable) != 'undefined' && config.sortable){
8567                 c.cls = 'sortable';
8568                 c.html = '<i class="fa"></i>' + c.html;
8569             }
8570             
8571             // could use BS4 hidden-..-down 
8572             
8573             if(typeof(config.lgHeader) != 'undefined'){
8574                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8575             }
8576             
8577             if(typeof(config.mdHeader) != 'undefined'){
8578                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8579             }
8580             
8581             if(typeof(config.smHeader) != 'undefined'){
8582                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8583             }
8584             
8585             if(typeof(config.xsHeader) != 'undefined'){
8586                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8587             }
8588             
8589             if(hh.length){
8590                 c.html = hh;
8591             }
8592             
8593             if(typeof(config.tooltip) != 'undefined'){
8594                 c.tooltip = config.tooltip;
8595             }
8596             
8597             if(typeof(config.colspan) != 'undefined'){
8598                 c.colspan = config.colspan;
8599             }
8600             
8601             if(typeof(config.hidden) != 'undefined' && config.hidden){
8602                 c.style += ' display:none;';
8603             }
8604             
8605             if(typeof(config.dataIndex) != 'undefined'){
8606                 c.sort = config.dataIndex;
8607             }
8608             
8609            
8610             
8611             if(typeof(config.align) != 'undefined' && config.align.length){
8612                 c.style += ' text-align:' + config.align + ';';
8613             }
8614             
8615             if(typeof(config.width) != 'undefined'){
8616                 c.style += ' width:' + config.width + 'px;';
8617                 this.totalWidth += config.width;
8618             } else {
8619                 this.totalWidth += 100; // assume minimum of 100 per column?
8620             }
8621             
8622             if(typeof(config.cls) != 'undefined'){
8623                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8624             }
8625             
8626             ['xs','sm','md','lg'].map(function(size){
8627                 
8628                 if(typeof(config[size]) == 'undefined'){
8629                     return;
8630                 }
8631                  
8632                 if (!config[size]) { // 0 = hidden
8633                     // BS 4 '0' is treated as hide that column and below.
8634                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8635                     return;
8636                 }
8637                 
8638                 c.cls += ' col-' + size + '-' + config[size] + (
8639                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8640                 );
8641                 
8642                 
8643             });
8644             
8645             header.cn.push(c)
8646         }
8647         
8648         return header;
8649     },
8650     
8651     renderBody : function()
8652     {
8653         var body = {
8654             tag: 'tbody',
8655             cn : [
8656                 {
8657                     tag: 'tr',
8658                     cn : [
8659                         {
8660                             tag : 'td',
8661                             colspan :  this.cm.getColumnCount()
8662                         }
8663                     ]
8664                 }
8665             ]
8666         };
8667         
8668         return body;
8669     },
8670     
8671     renderFooter : function()
8672     {
8673         var footer = {
8674             tag: 'tfoot',
8675             cn : [
8676                 {
8677                     tag: 'tr',
8678                     cn : [
8679                         {
8680                             tag : 'td',
8681                             colspan :  this.cm.getColumnCount()
8682                         }
8683                     ]
8684                 }
8685             ]
8686         };
8687         
8688         return footer;
8689     },
8690     
8691     
8692     
8693     onLoad : function()
8694     {
8695 //        Roo.log('ds onload');
8696         this.clear();
8697         
8698         var _this = this;
8699         var cm = this.cm;
8700         var ds = this.store;
8701         
8702         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8703             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8704             if (_this.store.sortInfo) {
8705                     
8706                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8707                     e.select('i', true).addClass(['fa-arrow-up']);
8708                 }
8709                 
8710                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8711                     e.select('i', true).addClass(['fa-arrow-down']);
8712                 }
8713             }
8714         });
8715         
8716         var tbody =  this.mainBody;
8717               
8718         if(ds.getCount() > 0){
8719             ds.data.each(function(d,rowIndex){
8720                 var row =  this.renderRow(cm, ds, rowIndex);
8721                 
8722                 tbody.createChild(row);
8723                 
8724                 var _this = this;
8725                 
8726                 if(row.cellObjects.length){
8727                     Roo.each(row.cellObjects, function(r){
8728                         _this.renderCellObject(r);
8729                     })
8730                 }
8731                 
8732             }, this);
8733         }
8734         
8735         var tfoot = this.el.select('tfoot', true).first();
8736         
8737         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8738             
8739             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8740             
8741             var total = this.ds.getTotalCount();
8742             
8743             if(this.footer.pageSize < total){
8744                 this.mainFoot.show();
8745             }
8746         }
8747         
8748         Roo.each(this.el.select('tbody td', true).elements, function(e){
8749             e.on('mouseover', _this.onMouseover, _this);
8750         });
8751         
8752         Roo.each(this.el.select('tbody td', true).elements, function(e){
8753             e.on('mouseout', _this.onMouseout, _this);
8754         });
8755         this.fireEvent('rowsrendered', this);
8756         
8757         this.autoSize();
8758     },
8759     
8760     
8761     onUpdate : function(ds,record)
8762     {
8763         this.refreshRow(record);
8764         this.autoSize();
8765     },
8766     
8767     onRemove : function(ds, record, index, isUpdate){
8768         if(isUpdate !== true){
8769             this.fireEvent("beforerowremoved", this, index, record);
8770         }
8771         var bt = this.mainBody.dom;
8772         
8773         var rows = this.el.select('tbody > tr', true).elements;
8774         
8775         if(typeof(rows[index]) != 'undefined'){
8776             bt.removeChild(rows[index].dom);
8777         }
8778         
8779 //        if(bt.rows[index]){
8780 //            bt.removeChild(bt.rows[index]);
8781 //        }
8782         
8783         if(isUpdate !== true){
8784             //this.stripeRows(index);
8785             //this.syncRowHeights(index, index);
8786             //this.layout();
8787             this.fireEvent("rowremoved", this, index, record);
8788         }
8789     },
8790     
8791     onAdd : function(ds, records, rowIndex)
8792     {
8793         //Roo.log('on Add called');
8794         // - note this does not handle multiple adding very well..
8795         var bt = this.mainBody.dom;
8796         for (var i =0 ; i < records.length;i++) {
8797             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8798             //Roo.log(records[i]);
8799             //Roo.log(this.store.getAt(rowIndex+i));
8800             this.insertRow(this.store, rowIndex + i, false);
8801             return;
8802         }
8803         
8804     },
8805     
8806     
8807     refreshRow : function(record){
8808         var ds = this.store, index;
8809         if(typeof record == 'number'){
8810             index = record;
8811             record = ds.getAt(index);
8812         }else{
8813             index = ds.indexOf(record);
8814             if (index < 0) {
8815                 return; // should not happen - but seems to 
8816             }
8817         }
8818         this.insertRow(ds, index, true);
8819         this.autoSize();
8820         this.onRemove(ds, record, index+1, true);
8821         this.autoSize();
8822         //this.syncRowHeights(index, index);
8823         //this.layout();
8824         this.fireEvent("rowupdated", this, index, record);
8825     },
8826     
8827     insertRow : function(dm, rowIndex, isUpdate){
8828         
8829         if(!isUpdate){
8830             this.fireEvent("beforerowsinserted", this, rowIndex);
8831         }
8832             //var s = this.getScrollState();
8833         var row = this.renderRow(this.cm, this.store, rowIndex);
8834         // insert before rowIndex..
8835         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8836         
8837         var _this = this;
8838                 
8839         if(row.cellObjects.length){
8840             Roo.each(row.cellObjects, function(r){
8841                 _this.renderCellObject(r);
8842             })
8843         }
8844             
8845         if(!isUpdate){
8846             this.fireEvent("rowsinserted", this, rowIndex);
8847             //this.syncRowHeights(firstRow, lastRow);
8848             //this.stripeRows(firstRow);
8849             //this.layout();
8850         }
8851         
8852     },
8853     
8854     
8855     getRowDom : function(rowIndex)
8856     {
8857         var rows = this.el.select('tbody > tr', true).elements;
8858         
8859         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8860         
8861     },
8862     // returns the object tree for a tr..
8863   
8864     
8865     renderRow : function(cm, ds, rowIndex) 
8866     {
8867         var d = ds.getAt(rowIndex);
8868         
8869         var row = {
8870             tag : 'tr',
8871             cls : 'x-row-' + rowIndex,
8872             cn : []
8873         };
8874             
8875         var cellObjects = [];
8876         
8877         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8878             var config = cm.config[i];
8879             
8880             var renderer = cm.getRenderer(i);
8881             var value = '';
8882             var id = false;
8883             
8884             if(typeof(renderer) !== 'undefined'){
8885                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8886             }
8887             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8888             // and are rendered into the cells after the row is rendered - using the id for the element.
8889             
8890             if(typeof(value) === 'object'){
8891                 id = Roo.id();
8892                 cellObjects.push({
8893                     container : id,
8894                     cfg : value 
8895                 })
8896             }
8897             
8898             var rowcfg = {
8899                 record: d,
8900                 rowIndex : rowIndex,
8901                 colIndex : i,
8902                 rowClass : ''
8903             };
8904
8905             this.fireEvent('rowclass', this, rowcfg);
8906             
8907             var td = {
8908                 tag: 'td',
8909                 // this might end up displaying HTML?
8910                 // this is too messy... - better to only do it on columsn you know are going to be too long
8911                 //tooltip : (typeof(value) === 'object') ? '' : value,
8912                 cls : rowcfg.rowClass + ' x-col-' + i,
8913                 style: '',
8914                 html: (typeof(value) === 'object') ? '' : value
8915             };
8916             
8917             if (id) {
8918                 td.id = id;
8919             }
8920             
8921             if(typeof(config.colspan) != 'undefined'){
8922                 td.colspan = config.colspan;
8923             }
8924             
8925             if(typeof(config.hidden) != 'undefined' && config.hidden){
8926                 td.style += ' display:none;';
8927             }
8928             
8929             if(typeof(config.align) != 'undefined' && config.align.length){
8930                 td.style += ' text-align:' + config.align + ';';
8931             }
8932             if(typeof(config.valign) != 'undefined' && config.valign.length){
8933                 td.style += ' vertical-align:' + config.valign + ';';
8934             }
8935             
8936             if(typeof(config.width) != 'undefined'){
8937                 td.style += ' width:' +  config.width + 'px;';
8938             }
8939             
8940             if(typeof(config.cursor) != 'undefined'){
8941                 td.style += ' cursor:' +  config.cursor + ';';
8942             }
8943             
8944             if(typeof(config.cls) != 'undefined'){
8945                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8946             }
8947             
8948             ['xs','sm','md','lg'].map(function(size){
8949                 
8950                 if(typeof(config[size]) == 'undefined'){
8951                     return;
8952                 }
8953                 
8954                 
8955                   
8956                 if (!config[size]) { // 0 = hidden
8957                     // BS 4 '0' is treated as hide that column and below.
8958                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8959                     return;
8960                 }
8961                 
8962                 td.cls += ' col-' + size + '-' + config[size] + (
8963                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8964                 );
8965                  
8966
8967             });
8968             
8969             row.cn.push(td);
8970            
8971         }
8972         
8973         row.cellObjects = cellObjects;
8974         
8975         return row;
8976           
8977     },
8978     
8979     
8980     
8981     onBeforeLoad : function()
8982     {
8983         
8984     },
8985      /**
8986      * Remove all rows
8987      */
8988     clear : function()
8989     {
8990         this.el.select('tbody', true).first().dom.innerHTML = '';
8991     },
8992     /**
8993      * Show or hide a row.
8994      * @param {Number} rowIndex to show or hide
8995      * @param {Boolean} state hide
8996      */
8997     setRowVisibility : function(rowIndex, state)
8998     {
8999         var bt = this.mainBody.dom;
9000         
9001         var rows = this.el.select('tbody > tr', true).elements;
9002         
9003         if(typeof(rows[rowIndex]) == 'undefined'){
9004             return;
9005         }
9006         rows[rowIndex].dom.style.display = state ? '' : 'none';
9007     },
9008     
9009     
9010     getSelectionModel : function(){
9011         if(!this.selModel){
9012             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9013         }
9014         return this.selModel;
9015     },
9016     /*
9017      * Render the Roo.bootstrap object from renderder
9018      */
9019     renderCellObject : function(r)
9020     {
9021         var _this = this;
9022         
9023         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9024         
9025         var t = r.cfg.render(r.container);
9026         
9027         if(r.cfg.cn){
9028             Roo.each(r.cfg.cn, function(c){
9029                 var child = {
9030                     container: t.getChildContainer(),
9031                     cfg: c
9032                 };
9033                 _this.renderCellObject(child);
9034             })
9035         }
9036     },
9037     
9038     getRowIndex : function(row)
9039     {
9040         var rowIndex = -1;
9041         
9042         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9043             if(el != row){
9044                 return;
9045             }
9046             
9047             rowIndex = index;
9048         });
9049         
9050         return rowIndex;
9051     },
9052      /**
9053      * Returns the grid's underlying element = used by panel.Grid
9054      * @return {Element} The element
9055      */
9056     getGridEl : function(){
9057         return this.el;
9058     },
9059      /**
9060      * Forces a resize - used by panel.Grid
9061      * @return {Element} The element
9062      */
9063     autoSize : function()
9064     {
9065         //var ctr = Roo.get(this.container.dom.parentElement);
9066         var ctr = Roo.get(this.el.dom);
9067         
9068         var thd = this.getGridEl().select('thead',true).first();
9069         var tbd = this.getGridEl().select('tbody', true).first();
9070         var tfd = this.getGridEl().select('tfoot', true).first();
9071         
9072         var cw = ctr.getWidth();
9073         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9074         
9075         if (tbd) {
9076             
9077             tbd.setWidth(ctr.getWidth());
9078             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9079             // this needs fixing for various usage - currently only hydra job advers I think..
9080             //tdb.setHeight(
9081             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9082             //); 
9083             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9084             cw -= barsize;
9085         }
9086         cw = Math.max(cw, this.totalWidth);
9087         this.getGridEl().select('tbody tr',true).setWidth(cw);
9088         
9089         // resize 'expandable coloumn?
9090         
9091         return; // we doe not have a view in this design..
9092         
9093     },
9094     onBodyScroll: function()
9095     {
9096         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9097         if(this.mainHead){
9098             this.mainHead.setStyle({
9099                 'position' : 'relative',
9100                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9101             });
9102         }
9103         
9104         if(this.lazyLoad){
9105             
9106             var scrollHeight = this.mainBody.dom.scrollHeight;
9107             
9108             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9109             
9110             var height = this.mainBody.getHeight();
9111             
9112             if(scrollHeight - height == scrollTop) {
9113                 
9114                 var total = this.ds.getTotalCount();
9115                 
9116                 if(this.footer.cursor + this.footer.pageSize < total){
9117                     
9118                     this.footer.ds.load({
9119                         params : {
9120                             start : this.footer.cursor + this.footer.pageSize,
9121                             limit : this.footer.pageSize
9122                         },
9123                         add : true
9124                     });
9125                 }
9126             }
9127             
9128         }
9129     },
9130     
9131     onHeaderChange : function()
9132     {
9133         var header = this.renderHeader();
9134         var table = this.el.select('table', true).first();
9135         
9136         this.mainHead.remove();
9137         this.mainHead = table.createChild(header, this.mainBody, false);
9138         
9139         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9140             e.on('click', this.sort, this);
9141         }, this);
9142         
9143         
9144     },
9145     
9146     onHiddenChange : function(colModel, colIndex, hidden)
9147     {
9148         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9149         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9150         
9151         this.CSS.updateRule(thSelector, "display", "");
9152         this.CSS.updateRule(tdSelector, "display", "");
9153         
9154         if(hidden){
9155             this.CSS.updateRule(thSelector, "display", "none");
9156             this.CSS.updateRule(tdSelector, "display", "none");
9157         }
9158         
9159         this.onHeaderChange();
9160         this.onLoad();
9161     },
9162     
9163     setColumnWidth: function(col_index, width)
9164     {
9165         // width = "md-2 xs-2..."
9166         if(!this.colModel.config[col_index]) {
9167             return;
9168         }
9169         
9170         var w = width.split(" ");
9171         
9172         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9173         
9174         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9175         
9176         
9177         for(var j = 0; j < w.length; j++) {
9178             
9179             if(!w[j]) {
9180                 continue;
9181             }
9182             
9183             var size_cls = w[j].split("-");
9184             
9185             if(!Number.isInteger(size_cls[1] * 1)) {
9186                 continue;
9187             }
9188             
9189             if(!this.colModel.config[col_index][size_cls[0]]) {
9190                 continue;
9191             }
9192             
9193             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9194                 continue;
9195             }
9196             
9197             h_row[0].classList.replace(
9198                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9199                 "col-"+size_cls[0]+"-"+size_cls[1]
9200             );
9201             
9202             for(var i = 0; i < rows.length; i++) {
9203                 
9204                 var size_cls = w[j].split("-");
9205                 
9206                 if(!Number.isInteger(size_cls[1] * 1)) {
9207                     continue;
9208                 }
9209                 
9210                 if(!this.colModel.config[col_index][size_cls[0]]) {
9211                     continue;
9212                 }
9213                 
9214                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9215                     continue;
9216                 }
9217                 
9218                 rows[i].classList.replace(
9219                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9220                     "col-"+size_cls[0]+"-"+size_cls[1]
9221                 );
9222             }
9223             
9224             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9225         }
9226     }
9227 });
9228
9229  
9230
9231  /*
9232  * - LGPL
9233  *
9234  * table cell
9235  * 
9236  */
9237
9238 /**
9239  * @class Roo.bootstrap.TableCell
9240  * @extends Roo.bootstrap.Component
9241  * Bootstrap TableCell class
9242  * @cfg {String} html cell contain text
9243  * @cfg {String} cls cell class
9244  * @cfg {String} tag cell tag (td|th) default td
9245  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9246  * @cfg {String} align Aligns the content in a cell
9247  * @cfg {String} axis Categorizes cells
9248  * @cfg {String} bgcolor Specifies the background color of a cell
9249  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9250  * @cfg {Number} colspan Specifies the number of columns a cell should span
9251  * @cfg {String} headers Specifies one or more header cells a cell is related to
9252  * @cfg {Number} height Sets the height of a cell
9253  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9254  * @cfg {Number} rowspan Sets the number of rows a cell should span
9255  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9256  * @cfg {String} valign Vertical aligns the content in a cell
9257  * @cfg {Number} width Specifies the width of a cell
9258  * 
9259  * @constructor
9260  * Create a new TableCell
9261  * @param {Object} config The config object
9262  */
9263
9264 Roo.bootstrap.TableCell = function(config){
9265     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9266 };
9267
9268 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9269     
9270     html: false,
9271     cls: false,
9272     tag: false,
9273     abbr: false,
9274     align: false,
9275     axis: false,
9276     bgcolor: false,
9277     charoff: false,
9278     colspan: false,
9279     headers: false,
9280     height: false,
9281     nowrap: false,
9282     rowspan: false,
9283     scope: false,
9284     valign: false,
9285     width: false,
9286     
9287     
9288     getAutoCreate : function(){
9289         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9290         
9291         cfg = {
9292             tag: 'td'
9293         };
9294         
9295         if(this.tag){
9296             cfg.tag = this.tag;
9297         }
9298         
9299         if (this.html) {
9300             cfg.html=this.html
9301         }
9302         if (this.cls) {
9303             cfg.cls=this.cls
9304         }
9305         if (this.abbr) {
9306             cfg.abbr=this.abbr
9307         }
9308         if (this.align) {
9309             cfg.align=this.align
9310         }
9311         if (this.axis) {
9312             cfg.axis=this.axis
9313         }
9314         if (this.bgcolor) {
9315             cfg.bgcolor=this.bgcolor
9316         }
9317         if (this.charoff) {
9318             cfg.charoff=this.charoff
9319         }
9320         if (this.colspan) {
9321             cfg.colspan=this.colspan
9322         }
9323         if (this.headers) {
9324             cfg.headers=this.headers
9325         }
9326         if (this.height) {
9327             cfg.height=this.height
9328         }
9329         if (this.nowrap) {
9330             cfg.nowrap=this.nowrap
9331         }
9332         if (this.rowspan) {
9333             cfg.rowspan=this.rowspan
9334         }
9335         if (this.scope) {
9336             cfg.scope=this.scope
9337         }
9338         if (this.valign) {
9339             cfg.valign=this.valign
9340         }
9341         if (this.width) {
9342             cfg.width=this.width
9343         }
9344         
9345         
9346         return cfg;
9347     }
9348    
9349 });
9350
9351  
9352
9353  /*
9354  * - LGPL
9355  *
9356  * table row
9357  * 
9358  */
9359
9360 /**
9361  * @class Roo.bootstrap.TableRow
9362  * @extends Roo.bootstrap.Component
9363  * Bootstrap TableRow class
9364  * @cfg {String} cls row class
9365  * @cfg {String} align Aligns the content in a table row
9366  * @cfg {String} bgcolor Specifies a background color for a table row
9367  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9368  * @cfg {String} valign Vertical aligns the content in a table row
9369  * 
9370  * @constructor
9371  * Create a new TableRow
9372  * @param {Object} config The config object
9373  */
9374
9375 Roo.bootstrap.TableRow = function(config){
9376     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9377 };
9378
9379 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9380     
9381     cls: false,
9382     align: false,
9383     bgcolor: false,
9384     charoff: false,
9385     valign: false,
9386     
9387     getAutoCreate : function(){
9388         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9389         
9390         cfg = {
9391             tag: 'tr'
9392         };
9393             
9394         if(this.cls){
9395             cfg.cls = this.cls;
9396         }
9397         if(this.align){
9398             cfg.align = this.align;
9399         }
9400         if(this.bgcolor){
9401             cfg.bgcolor = this.bgcolor;
9402         }
9403         if(this.charoff){
9404             cfg.charoff = this.charoff;
9405         }
9406         if(this.valign){
9407             cfg.valign = this.valign;
9408         }
9409         
9410         return cfg;
9411     }
9412    
9413 });
9414
9415  
9416
9417  /*
9418  * - LGPL
9419  *
9420  * table body
9421  * 
9422  */
9423
9424 /**
9425  * @class Roo.bootstrap.TableBody
9426  * @extends Roo.bootstrap.Component
9427  * Bootstrap TableBody class
9428  * @cfg {String} cls element class
9429  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9430  * @cfg {String} align Aligns the content inside the element
9431  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9432  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9433  * 
9434  * @constructor
9435  * Create a new TableBody
9436  * @param {Object} config The config object
9437  */
9438
9439 Roo.bootstrap.TableBody = function(config){
9440     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9441 };
9442
9443 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9444     
9445     cls: false,
9446     tag: false,
9447     align: false,
9448     charoff: false,
9449     valign: false,
9450     
9451     getAutoCreate : function(){
9452         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9453         
9454         cfg = {
9455             tag: 'tbody'
9456         };
9457             
9458         if (this.cls) {
9459             cfg.cls=this.cls
9460         }
9461         if(this.tag){
9462             cfg.tag = this.tag;
9463         }
9464         
9465         if(this.align){
9466             cfg.align = this.align;
9467         }
9468         if(this.charoff){
9469             cfg.charoff = this.charoff;
9470         }
9471         if(this.valign){
9472             cfg.valign = this.valign;
9473         }
9474         
9475         return cfg;
9476     }
9477     
9478     
9479 //    initEvents : function()
9480 //    {
9481 //        
9482 //        if(!this.store){
9483 //            return;
9484 //        }
9485 //        
9486 //        this.store = Roo.factory(this.store, Roo.data);
9487 //        this.store.on('load', this.onLoad, this);
9488 //        
9489 //        this.store.load();
9490 //        
9491 //    },
9492 //    
9493 //    onLoad: function () 
9494 //    {   
9495 //        this.fireEvent('load', this);
9496 //    }
9497 //    
9498 //   
9499 });
9500
9501  
9502
9503  /*
9504  * Based on:
9505  * Ext JS Library 1.1.1
9506  * Copyright(c) 2006-2007, Ext JS, LLC.
9507  *
9508  * Originally Released Under LGPL - original licence link has changed is not relivant.
9509  *
9510  * Fork - LGPL
9511  * <script type="text/javascript">
9512  */
9513
9514 // as we use this in bootstrap.
9515 Roo.namespace('Roo.form');
9516  /**
9517  * @class Roo.form.Action
9518  * Internal Class used to handle form actions
9519  * @constructor
9520  * @param {Roo.form.BasicForm} el The form element or its id
9521  * @param {Object} config Configuration options
9522  */
9523
9524  
9525  
9526 // define the action interface
9527 Roo.form.Action = function(form, options){
9528     this.form = form;
9529     this.options = options || {};
9530 };
9531 /**
9532  * Client Validation Failed
9533  * @const 
9534  */
9535 Roo.form.Action.CLIENT_INVALID = 'client';
9536 /**
9537  * Server Validation Failed
9538  * @const 
9539  */
9540 Roo.form.Action.SERVER_INVALID = 'server';
9541  /**
9542  * Connect to Server Failed
9543  * @const 
9544  */
9545 Roo.form.Action.CONNECT_FAILURE = 'connect';
9546 /**
9547  * Reading Data from Server Failed
9548  * @const 
9549  */
9550 Roo.form.Action.LOAD_FAILURE = 'load';
9551
9552 Roo.form.Action.prototype = {
9553     type : 'default',
9554     failureType : undefined,
9555     response : undefined,
9556     result : undefined,
9557
9558     // interface method
9559     run : function(options){
9560
9561     },
9562
9563     // interface method
9564     success : function(response){
9565
9566     },
9567
9568     // interface method
9569     handleResponse : function(response){
9570
9571     },
9572
9573     // default connection failure
9574     failure : function(response){
9575         
9576         this.response = response;
9577         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9578         this.form.afterAction(this, false);
9579     },
9580
9581     processResponse : function(response){
9582         this.response = response;
9583         if(!response.responseText){
9584             return true;
9585         }
9586         this.result = this.handleResponse(response);
9587         return this.result;
9588     },
9589
9590     // utility functions used internally
9591     getUrl : function(appendParams){
9592         var url = this.options.url || this.form.url || this.form.el.dom.action;
9593         if(appendParams){
9594             var p = this.getParams();
9595             if(p){
9596                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9597             }
9598         }
9599         return url;
9600     },
9601
9602     getMethod : function(){
9603         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9604     },
9605
9606     getParams : function(){
9607         var bp = this.form.baseParams;
9608         var p = this.options.params;
9609         if(p){
9610             if(typeof p == "object"){
9611                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9612             }else if(typeof p == 'string' && bp){
9613                 p += '&' + Roo.urlEncode(bp);
9614             }
9615         }else if(bp){
9616             p = Roo.urlEncode(bp);
9617         }
9618         return p;
9619     },
9620
9621     createCallback : function(){
9622         return {
9623             success: this.success,
9624             failure: this.failure,
9625             scope: this,
9626             timeout: (this.form.timeout*1000),
9627             upload: this.form.fileUpload ? this.success : undefined
9628         };
9629     }
9630 };
9631
9632 Roo.form.Action.Submit = function(form, options){
9633     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9634 };
9635
9636 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9637     type : 'submit',
9638
9639     haveProgress : false,
9640     uploadComplete : false,
9641     
9642     // uploadProgress indicator.
9643     uploadProgress : function()
9644     {
9645         if (!this.form.progressUrl) {
9646             return;
9647         }
9648         
9649         if (!this.haveProgress) {
9650             Roo.MessageBox.progress("Uploading", "Uploading");
9651         }
9652         if (this.uploadComplete) {
9653            Roo.MessageBox.hide();
9654            return;
9655         }
9656         
9657         this.haveProgress = true;
9658    
9659         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9660         
9661         var c = new Roo.data.Connection();
9662         c.request({
9663             url : this.form.progressUrl,
9664             params: {
9665                 id : uid
9666             },
9667             method: 'GET',
9668             success : function(req){
9669                //console.log(data);
9670                 var rdata = false;
9671                 var edata;
9672                 try  {
9673                    rdata = Roo.decode(req.responseText)
9674                 } catch (e) {
9675                     Roo.log("Invalid data from server..");
9676                     Roo.log(edata);
9677                     return;
9678                 }
9679                 if (!rdata || !rdata.success) {
9680                     Roo.log(rdata);
9681                     Roo.MessageBox.alert(Roo.encode(rdata));
9682                     return;
9683                 }
9684                 var data = rdata.data;
9685                 
9686                 if (this.uploadComplete) {
9687                    Roo.MessageBox.hide();
9688                    return;
9689                 }
9690                    
9691                 if (data){
9692                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9693                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9694                     );
9695                 }
9696                 this.uploadProgress.defer(2000,this);
9697             },
9698        
9699             failure: function(data) {
9700                 Roo.log('progress url failed ');
9701                 Roo.log(data);
9702             },
9703             scope : this
9704         });
9705            
9706     },
9707     
9708     
9709     run : function()
9710     {
9711         // run get Values on the form, so it syncs any secondary forms.
9712         this.form.getValues();
9713         
9714         var o = this.options;
9715         var method = this.getMethod();
9716         var isPost = method == 'POST';
9717         if(o.clientValidation === false || this.form.isValid()){
9718             
9719             if (this.form.progressUrl) {
9720                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9721                     (new Date() * 1) + '' + Math.random());
9722                     
9723             } 
9724             
9725             
9726             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9727                 form:this.form.el.dom,
9728                 url:this.getUrl(!isPost),
9729                 method: method,
9730                 params:isPost ? this.getParams() : null,
9731                 isUpload: this.form.fileUpload,
9732                 formData : this.form.formData
9733             }));
9734             
9735             this.uploadProgress();
9736
9737         }else if (o.clientValidation !== false){ // client validation failed
9738             this.failureType = Roo.form.Action.CLIENT_INVALID;
9739             this.form.afterAction(this, false);
9740         }
9741     },
9742
9743     success : function(response)
9744     {
9745         this.uploadComplete= true;
9746         if (this.haveProgress) {
9747             Roo.MessageBox.hide();
9748         }
9749         
9750         
9751         var result = this.processResponse(response);
9752         if(result === true || result.success){
9753             this.form.afterAction(this, true);
9754             return;
9755         }
9756         if(result.errors){
9757             this.form.markInvalid(result.errors);
9758             this.failureType = Roo.form.Action.SERVER_INVALID;
9759         }
9760         this.form.afterAction(this, false);
9761     },
9762     failure : function(response)
9763     {
9764         this.uploadComplete= true;
9765         if (this.haveProgress) {
9766             Roo.MessageBox.hide();
9767         }
9768         
9769         this.response = response;
9770         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9771         this.form.afterAction(this, false);
9772     },
9773     
9774     handleResponse : function(response){
9775         if(this.form.errorReader){
9776             var rs = this.form.errorReader.read(response);
9777             var errors = [];
9778             if(rs.records){
9779                 for(var i = 0, len = rs.records.length; i < len; i++) {
9780                     var r = rs.records[i];
9781                     errors[i] = r.data;
9782                 }
9783             }
9784             if(errors.length < 1){
9785                 errors = null;
9786             }
9787             return {
9788                 success : rs.success,
9789                 errors : errors
9790             };
9791         }
9792         var ret = false;
9793         try {
9794             ret = Roo.decode(response.responseText);
9795         } catch (e) {
9796             ret = {
9797                 success: false,
9798                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9799                 errors : []
9800             };
9801         }
9802         return ret;
9803         
9804     }
9805 });
9806
9807
9808 Roo.form.Action.Load = function(form, options){
9809     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9810     this.reader = this.form.reader;
9811 };
9812
9813 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9814     type : 'load',
9815
9816     run : function(){
9817         
9818         Roo.Ajax.request(Roo.apply(
9819                 this.createCallback(), {
9820                     method:this.getMethod(),
9821                     url:this.getUrl(false),
9822                     params:this.getParams()
9823         }));
9824     },
9825
9826     success : function(response){
9827         
9828         var result = this.processResponse(response);
9829         if(result === true || !result.success || !result.data){
9830             this.failureType = Roo.form.Action.LOAD_FAILURE;
9831             this.form.afterAction(this, false);
9832             return;
9833         }
9834         this.form.clearInvalid();
9835         this.form.setValues(result.data);
9836         this.form.afterAction(this, true);
9837     },
9838
9839     handleResponse : function(response){
9840         if(this.form.reader){
9841             var rs = this.form.reader.read(response);
9842             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9843             return {
9844                 success : rs.success,
9845                 data : data
9846             };
9847         }
9848         return Roo.decode(response.responseText);
9849     }
9850 });
9851
9852 Roo.form.Action.ACTION_TYPES = {
9853     'load' : Roo.form.Action.Load,
9854     'submit' : Roo.form.Action.Submit
9855 };/*
9856  * - LGPL
9857  *
9858  * form
9859  *
9860  */
9861
9862 /**
9863  * @class Roo.bootstrap.Form
9864  * @extends Roo.bootstrap.Component
9865  * Bootstrap Form class
9866  * @cfg {String} method  GET | POST (default POST)
9867  * @cfg {String} labelAlign top | left (default top)
9868  * @cfg {String} align left  | right - for navbars
9869  * @cfg {Boolean} loadMask load mask when submit (default true)
9870
9871  *
9872  * @constructor
9873  * Create a new Form
9874  * @param {Object} config The config object
9875  */
9876
9877
9878 Roo.bootstrap.Form = function(config){
9879     
9880     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9881     
9882     Roo.bootstrap.Form.popover.apply();
9883     
9884     this.addEvents({
9885         /**
9886          * @event clientvalidation
9887          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9888          * @param {Form} this
9889          * @param {Boolean} valid true if the form has passed client-side validation
9890          */
9891         clientvalidation: true,
9892         /**
9893          * @event beforeaction
9894          * Fires before any action is performed. Return false to cancel the action.
9895          * @param {Form} this
9896          * @param {Action} action The action to be performed
9897          */
9898         beforeaction: true,
9899         /**
9900          * @event actionfailed
9901          * Fires when an action fails.
9902          * @param {Form} this
9903          * @param {Action} action The action that failed
9904          */
9905         actionfailed : true,
9906         /**
9907          * @event actioncomplete
9908          * Fires when an action is completed.
9909          * @param {Form} this
9910          * @param {Action} action The action that completed
9911          */
9912         actioncomplete : true
9913     });
9914 };
9915
9916 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9917
9918      /**
9919      * @cfg {String} method
9920      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9921      */
9922     method : 'POST',
9923     /**
9924      * @cfg {String} url
9925      * The URL to use for form actions if one isn't supplied in the action options.
9926      */
9927     /**
9928      * @cfg {Boolean} fileUpload
9929      * Set to true if this form is a file upload.
9930      */
9931
9932     /**
9933      * @cfg {Object} baseParams
9934      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9935      */
9936
9937     /**
9938      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9939      */
9940     timeout: 30,
9941     /**
9942      * @cfg {Sting} align (left|right) for navbar forms
9943      */
9944     align : 'left',
9945
9946     // private
9947     activeAction : null,
9948
9949     /**
9950      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9951      * element by passing it or its id or mask the form itself by passing in true.
9952      * @type Mixed
9953      */
9954     waitMsgTarget : false,
9955
9956     loadMask : true,
9957     
9958     /**
9959      * @cfg {Boolean} errorMask (true|false) default false
9960      */
9961     errorMask : false,
9962     
9963     /**
9964      * @cfg {Number} maskOffset Default 100
9965      */
9966     maskOffset : 100,
9967     
9968     /**
9969      * @cfg {Boolean} maskBody
9970      */
9971     maskBody : false,
9972
9973     getAutoCreate : function(){
9974
9975         var cfg = {
9976             tag: 'form',
9977             method : this.method || 'POST',
9978             id : this.id || Roo.id(),
9979             cls : ''
9980         };
9981         if (this.parent().xtype.match(/^Nav/)) {
9982             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9983
9984         }
9985
9986         if (this.labelAlign == 'left' ) {
9987             cfg.cls += ' form-horizontal';
9988         }
9989
9990
9991         return cfg;
9992     },
9993     initEvents : function()
9994     {
9995         this.el.on('submit', this.onSubmit, this);
9996         // this was added as random key presses on the form where triggering form submit.
9997         this.el.on('keypress', function(e) {
9998             if (e.getCharCode() != 13) {
9999                 return true;
10000             }
10001             // we might need to allow it for textareas.. and some other items.
10002             // check e.getTarget().
10003
10004             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10005                 return true;
10006             }
10007
10008             Roo.log("keypress blocked");
10009
10010             e.preventDefault();
10011             return false;
10012         });
10013         
10014     },
10015     // private
10016     onSubmit : function(e){
10017         e.stopEvent();
10018     },
10019
10020      /**
10021      * Returns true if client-side validation on the form is successful.
10022      * @return Boolean
10023      */
10024     isValid : function(){
10025         var items = this.getItems();
10026         var valid = true;
10027         var target = false;
10028         
10029         items.each(function(f){
10030             
10031             if(f.validate()){
10032                 return;
10033             }
10034             
10035             Roo.log('invalid field: ' + f.name);
10036             
10037             valid = false;
10038
10039             if(!target && f.el.isVisible(true)){
10040                 target = f;
10041             }
10042            
10043         });
10044         
10045         if(this.errorMask && !valid){
10046             Roo.bootstrap.Form.popover.mask(this, target);
10047         }
10048         
10049         return valid;
10050     },
10051     
10052     /**
10053      * Returns true if any fields in this form have changed since their original load.
10054      * @return Boolean
10055      */
10056     isDirty : function(){
10057         var dirty = false;
10058         var items = this.getItems();
10059         items.each(function(f){
10060            if(f.isDirty()){
10061                dirty = true;
10062                return false;
10063            }
10064            return true;
10065         });
10066         return dirty;
10067     },
10068      /**
10069      * Performs a predefined action (submit or load) or custom actions you define on this form.
10070      * @param {String} actionName The name of the action type
10071      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10072      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10073      * accept other config options):
10074      * <pre>
10075 Property          Type             Description
10076 ----------------  ---------------  ----------------------------------------------------------------------------------
10077 url               String           The url for the action (defaults to the form's url)
10078 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10079 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10080 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10081                                    validate the form on the client (defaults to false)
10082      * </pre>
10083      * @return {BasicForm} this
10084      */
10085     doAction : function(action, options){
10086         if(typeof action == 'string'){
10087             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10088         }
10089         if(this.fireEvent('beforeaction', this, action) !== false){
10090             this.beforeAction(action);
10091             action.run.defer(100, action);
10092         }
10093         return this;
10094     },
10095
10096     // private
10097     beforeAction : function(action){
10098         var o = action.options;
10099         
10100         if(this.loadMask){
10101             
10102             if(this.maskBody){
10103                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10104             } else {
10105                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10106             }
10107         }
10108         // not really supported yet.. ??
10109
10110         //if(this.waitMsgTarget === true){
10111         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10112         //}else if(this.waitMsgTarget){
10113         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10114         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10115         //}else {
10116         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10117        // }
10118
10119     },
10120
10121     // private
10122     afterAction : function(action, success){
10123         this.activeAction = null;
10124         var o = action.options;
10125
10126         if(this.loadMask){
10127             
10128             if(this.maskBody){
10129                 Roo.get(document.body).unmask();
10130             } else {
10131                 this.el.unmask();
10132             }
10133         }
10134         
10135         //if(this.waitMsgTarget === true){
10136 //            this.el.unmask();
10137         //}else if(this.waitMsgTarget){
10138         //    this.waitMsgTarget.unmask();
10139         //}else{
10140         //    Roo.MessageBox.updateProgress(1);
10141         //    Roo.MessageBox.hide();
10142        // }
10143         //
10144         if(success){
10145             if(o.reset){
10146                 this.reset();
10147             }
10148             Roo.callback(o.success, o.scope, [this, action]);
10149             this.fireEvent('actioncomplete', this, action);
10150
10151         }else{
10152
10153             // failure condition..
10154             // we have a scenario where updates need confirming.
10155             // eg. if a locking scenario exists..
10156             // we look for { errors : { needs_confirm : true }} in the response.
10157             if (
10158                 (typeof(action.result) != 'undefined')  &&
10159                 (typeof(action.result.errors) != 'undefined')  &&
10160                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10161            ){
10162                 var _t = this;
10163                 Roo.log("not supported yet");
10164                  /*
10165
10166                 Roo.MessageBox.confirm(
10167                     "Change requires confirmation",
10168                     action.result.errorMsg,
10169                     function(r) {
10170                         if (r != 'yes') {
10171                             return;
10172                         }
10173                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10174                     }
10175
10176                 );
10177                 */
10178
10179
10180                 return;
10181             }
10182
10183             Roo.callback(o.failure, o.scope, [this, action]);
10184             // show an error message if no failed handler is set..
10185             if (!this.hasListener('actionfailed')) {
10186                 Roo.log("need to add dialog support");
10187                 /*
10188                 Roo.MessageBox.alert("Error",
10189                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10190                         action.result.errorMsg :
10191                         "Saving Failed, please check your entries or try again"
10192                 );
10193                 */
10194             }
10195
10196             this.fireEvent('actionfailed', this, action);
10197         }
10198
10199     },
10200     /**
10201      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10202      * @param {String} id The value to search for
10203      * @return Field
10204      */
10205     findField : function(id){
10206         var items = this.getItems();
10207         var field = items.get(id);
10208         if(!field){
10209              items.each(function(f){
10210                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10211                     field = f;
10212                     return false;
10213                 }
10214                 return true;
10215             });
10216         }
10217         return field || null;
10218     },
10219      /**
10220      * Mark fields in this form invalid in bulk.
10221      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10222      * @return {BasicForm} this
10223      */
10224     markInvalid : function(errors){
10225         if(errors instanceof Array){
10226             for(var i = 0, len = errors.length; i < len; i++){
10227                 var fieldError = errors[i];
10228                 var f = this.findField(fieldError.id);
10229                 if(f){
10230                     f.markInvalid(fieldError.msg);
10231                 }
10232             }
10233         }else{
10234             var field, id;
10235             for(id in errors){
10236                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10237                     field.markInvalid(errors[id]);
10238                 }
10239             }
10240         }
10241         //Roo.each(this.childForms || [], function (f) {
10242         //    f.markInvalid(errors);
10243         //});
10244
10245         return this;
10246     },
10247
10248     /**
10249      * Set values for fields in this form in bulk.
10250      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10251      * @return {BasicForm} this
10252      */
10253     setValues : function(values){
10254         if(values instanceof Array){ // array of objects
10255             for(var i = 0, len = values.length; i < len; i++){
10256                 var v = values[i];
10257                 var f = this.findField(v.id);
10258                 if(f){
10259                     f.setValue(v.value);
10260                     if(this.trackResetOnLoad){
10261                         f.originalValue = f.getValue();
10262                     }
10263                 }
10264             }
10265         }else{ // object hash
10266             var field, id;
10267             for(id in values){
10268                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10269
10270                     if (field.setFromData &&
10271                         field.valueField &&
10272                         field.displayField &&
10273                         // combos' with local stores can
10274                         // be queried via setValue()
10275                         // to set their value..
10276                         (field.store && !field.store.isLocal)
10277                         ) {
10278                         // it's a combo
10279                         var sd = { };
10280                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10281                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10282                         field.setFromData(sd);
10283
10284                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10285                         
10286                         field.setFromData(values);
10287                         
10288                     } else {
10289                         field.setValue(values[id]);
10290                     }
10291
10292
10293                     if(this.trackResetOnLoad){
10294                         field.originalValue = field.getValue();
10295                     }
10296                 }
10297             }
10298         }
10299
10300         //Roo.each(this.childForms || [], function (f) {
10301         //    f.setValues(values);
10302         //});
10303
10304         return this;
10305     },
10306
10307     /**
10308      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10309      * they are returned as an array.
10310      * @param {Boolean} asString
10311      * @return {Object}
10312      */
10313     getValues : function(asString){
10314         //if (this.childForms) {
10315             // copy values from the child forms
10316         //    Roo.each(this.childForms, function (f) {
10317         //        this.setValues(f.getValues());
10318         //    }, this);
10319         //}
10320
10321
10322
10323         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10324         if(asString === true){
10325             return fs;
10326         }
10327         return Roo.urlDecode(fs);
10328     },
10329
10330     /**
10331      * Returns the fields in this form as an object with key/value pairs.
10332      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10333      * @return {Object}
10334      */
10335     getFieldValues : function(with_hidden)
10336     {
10337         var items = this.getItems();
10338         var ret = {};
10339         items.each(function(f){
10340             
10341             if (!f.getName()) {
10342                 return;
10343             }
10344             
10345             var v = f.getValue();
10346             
10347             if (f.inputType =='radio') {
10348                 if (typeof(ret[f.getName()]) == 'undefined') {
10349                     ret[f.getName()] = ''; // empty..
10350                 }
10351
10352                 if (!f.el.dom.checked) {
10353                     return;
10354
10355                 }
10356                 v = f.el.dom.value;
10357
10358             }
10359             
10360             if(f.xtype == 'MoneyField'){
10361                 ret[f.currencyName] = f.getCurrency();
10362             }
10363
10364             // not sure if this supported any more..
10365             if ((typeof(v) == 'object') && f.getRawValue) {
10366                 v = f.getRawValue() ; // dates..
10367             }
10368             // combo boxes where name != hiddenName...
10369             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10370                 ret[f.name] = f.getRawValue();
10371             }
10372             ret[f.getName()] = v;
10373         });
10374
10375         return ret;
10376     },
10377
10378     /**
10379      * Clears all invalid messages in this form.
10380      * @return {BasicForm} this
10381      */
10382     clearInvalid : function(){
10383         var items = this.getItems();
10384
10385         items.each(function(f){
10386            f.clearInvalid();
10387         });
10388
10389         return this;
10390     },
10391
10392     /**
10393      * Resets this form.
10394      * @return {BasicForm} this
10395      */
10396     reset : function(){
10397         var items = this.getItems();
10398         items.each(function(f){
10399             f.reset();
10400         });
10401
10402         Roo.each(this.childForms || [], function (f) {
10403             f.reset();
10404         });
10405
10406
10407         return this;
10408     },
10409     
10410     getItems : function()
10411     {
10412         var r=new Roo.util.MixedCollection(false, function(o){
10413             return o.id || (o.id = Roo.id());
10414         });
10415         var iter = function(el) {
10416             if (el.inputEl) {
10417                 r.add(el);
10418             }
10419             if (!el.items) {
10420                 return;
10421             }
10422             Roo.each(el.items,function(e) {
10423                 iter(e);
10424             });
10425         };
10426
10427         iter(this);
10428         return r;
10429     },
10430     
10431     hideFields : function(items)
10432     {
10433         Roo.each(items, function(i){
10434             
10435             var f = this.findField(i);
10436             
10437             if(!f){
10438                 return;
10439             }
10440             
10441             f.hide();
10442             
10443         }, this);
10444     },
10445     
10446     showFields : function(items)
10447     {
10448         Roo.each(items, function(i){
10449             
10450             var f = this.findField(i);
10451             
10452             if(!f){
10453                 return;
10454             }
10455             
10456             f.show();
10457             
10458         }, this);
10459     }
10460
10461 });
10462
10463 Roo.apply(Roo.bootstrap.Form, {
10464     
10465     popover : {
10466         
10467         padding : 5,
10468         
10469         isApplied : false,
10470         
10471         isMasked : false,
10472         
10473         form : false,
10474         
10475         target : false,
10476         
10477         toolTip : false,
10478         
10479         intervalID : false,
10480         
10481         maskEl : false,
10482         
10483         apply : function()
10484         {
10485             if(this.isApplied){
10486                 return;
10487             }
10488             
10489             this.maskEl = {
10490                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10491                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10492                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10493                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10494             };
10495             
10496             this.maskEl.top.enableDisplayMode("block");
10497             this.maskEl.left.enableDisplayMode("block");
10498             this.maskEl.bottom.enableDisplayMode("block");
10499             this.maskEl.right.enableDisplayMode("block");
10500             
10501             this.toolTip = new Roo.bootstrap.Tooltip({
10502                 cls : 'roo-form-error-popover',
10503                 alignment : {
10504                     'left' : ['r-l', [-2,0], 'right'],
10505                     'right' : ['l-r', [2,0], 'left'],
10506                     'bottom' : ['tl-bl', [0,2], 'top'],
10507                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10508                 }
10509             });
10510             
10511             this.toolTip.render(Roo.get(document.body));
10512
10513             this.toolTip.el.enableDisplayMode("block");
10514             
10515             Roo.get(document.body).on('click', function(){
10516                 this.unmask();
10517             }, this);
10518             
10519             Roo.get(document.body).on('touchstart', function(){
10520                 this.unmask();
10521             }, this);
10522             
10523             this.isApplied = true
10524         },
10525         
10526         mask : function(form, target)
10527         {
10528             this.form = form;
10529             
10530             this.target = target;
10531             
10532             if(!this.form.errorMask || !target.el){
10533                 return;
10534             }
10535             
10536             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10537             
10538             Roo.log(scrollable);
10539             
10540             var ot = this.target.el.calcOffsetsTo(scrollable);
10541             
10542             var scrollTo = ot[1] - this.form.maskOffset;
10543             
10544             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10545             
10546             scrollable.scrollTo('top', scrollTo);
10547             
10548             var box = this.target.el.getBox();
10549             Roo.log(box);
10550             var zIndex = Roo.bootstrap.Modal.zIndex++;
10551
10552             
10553             this.maskEl.top.setStyle('position', 'absolute');
10554             this.maskEl.top.setStyle('z-index', zIndex);
10555             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10556             this.maskEl.top.setLeft(0);
10557             this.maskEl.top.setTop(0);
10558             this.maskEl.top.show();
10559             
10560             this.maskEl.left.setStyle('position', 'absolute');
10561             this.maskEl.left.setStyle('z-index', zIndex);
10562             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10563             this.maskEl.left.setLeft(0);
10564             this.maskEl.left.setTop(box.y - this.padding);
10565             this.maskEl.left.show();
10566
10567             this.maskEl.bottom.setStyle('position', 'absolute');
10568             this.maskEl.bottom.setStyle('z-index', zIndex);
10569             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10570             this.maskEl.bottom.setLeft(0);
10571             this.maskEl.bottom.setTop(box.bottom + this.padding);
10572             this.maskEl.bottom.show();
10573
10574             this.maskEl.right.setStyle('position', 'absolute');
10575             this.maskEl.right.setStyle('z-index', zIndex);
10576             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10577             this.maskEl.right.setLeft(box.right + this.padding);
10578             this.maskEl.right.setTop(box.y - this.padding);
10579             this.maskEl.right.show();
10580
10581             this.toolTip.bindEl = this.target.el;
10582
10583             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10584
10585             var tip = this.target.blankText;
10586
10587             if(this.target.getValue() !== '' ) {
10588                 
10589                 if (this.target.invalidText.length) {
10590                     tip = this.target.invalidText;
10591                 } else if (this.target.regexText.length){
10592                     tip = this.target.regexText;
10593                 }
10594             }
10595
10596             this.toolTip.show(tip);
10597
10598             this.intervalID = window.setInterval(function() {
10599                 Roo.bootstrap.Form.popover.unmask();
10600             }, 10000);
10601
10602             window.onwheel = function(){ return false;};
10603             
10604             (function(){ this.isMasked = true; }).defer(500, this);
10605             
10606         },
10607         
10608         unmask : function()
10609         {
10610             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10611                 return;
10612             }
10613             
10614             this.maskEl.top.setStyle('position', 'absolute');
10615             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10616             this.maskEl.top.hide();
10617
10618             this.maskEl.left.setStyle('position', 'absolute');
10619             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10620             this.maskEl.left.hide();
10621
10622             this.maskEl.bottom.setStyle('position', 'absolute');
10623             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10624             this.maskEl.bottom.hide();
10625
10626             this.maskEl.right.setStyle('position', 'absolute');
10627             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10628             this.maskEl.right.hide();
10629             
10630             this.toolTip.hide();
10631             
10632             this.toolTip.el.hide();
10633             
10634             window.onwheel = function(){ return true;};
10635             
10636             if(this.intervalID){
10637                 window.clearInterval(this.intervalID);
10638                 this.intervalID = false;
10639             }
10640             
10641             this.isMasked = false;
10642             
10643         }
10644         
10645     }
10646     
10647 });
10648
10649 /*
10650  * Based on:
10651  * Ext JS Library 1.1.1
10652  * Copyright(c) 2006-2007, Ext JS, LLC.
10653  *
10654  * Originally Released Under LGPL - original licence link has changed is not relivant.
10655  *
10656  * Fork - LGPL
10657  * <script type="text/javascript">
10658  */
10659 /**
10660  * @class Roo.form.VTypes
10661  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10662  * @singleton
10663  */
10664 Roo.form.VTypes = function(){
10665     // closure these in so they are only created once.
10666     var alpha = /^[a-zA-Z_]+$/;
10667     var alphanum = /^[a-zA-Z0-9_]+$/;
10668     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10669     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10670
10671     // All these messages and functions are configurable
10672     return {
10673         /**
10674          * The function used to validate email addresses
10675          * @param {String} value The email address
10676          */
10677         'email' : function(v){
10678             return email.test(v);
10679         },
10680         /**
10681          * The error text to display when the email validation function returns false
10682          * @type String
10683          */
10684         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10685         /**
10686          * The keystroke filter mask to be applied on email input
10687          * @type RegExp
10688          */
10689         'emailMask' : /[a-z0-9_\.\-@]/i,
10690
10691         /**
10692          * The function used to validate URLs
10693          * @param {String} value The URL
10694          */
10695         'url' : function(v){
10696             return url.test(v);
10697         },
10698         /**
10699          * The error text to display when the url validation function returns false
10700          * @type String
10701          */
10702         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10703         
10704         /**
10705          * The function used to validate alpha values
10706          * @param {String} value The value
10707          */
10708         'alpha' : function(v){
10709             return alpha.test(v);
10710         },
10711         /**
10712          * The error text to display when the alpha validation function returns false
10713          * @type String
10714          */
10715         'alphaText' : 'This field should only contain letters and _',
10716         /**
10717          * The keystroke filter mask to be applied on alpha input
10718          * @type RegExp
10719          */
10720         'alphaMask' : /[a-z_]/i,
10721
10722         /**
10723          * The function used to validate alphanumeric values
10724          * @param {String} value The value
10725          */
10726         'alphanum' : function(v){
10727             return alphanum.test(v);
10728         },
10729         /**
10730          * The error text to display when the alphanumeric validation function returns false
10731          * @type String
10732          */
10733         'alphanumText' : 'This field should only contain letters, numbers and _',
10734         /**
10735          * The keystroke filter mask to be applied on alphanumeric input
10736          * @type RegExp
10737          */
10738         'alphanumMask' : /[a-z0-9_]/i
10739     };
10740 }();/*
10741  * - LGPL
10742  *
10743  * Input
10744  * 
10745  */
10746
10747 /**
10748  * @class Roo.bootstrap.Input
10749  * @extends Roo.bootstrap.Component
10750  * Bootstrap Input class
10751  * @cfg {Boolean} disabled is it disabled
10752  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10753  * @cfg {String} name name of the input
10754  * @cfg {string} fieldLabel - the label associated
10755  * @cfg {string} placeholder - placeholder to put in text.
10756  * @cfg {string}  before - input group add on before
10757  * @cfg {string} after - input group add on after
10758  * @cfg {string} size - (lg|sm) or leave empty..
10759  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10760  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10761  * @cfg {Number} md colspan out of 12 for computer-sized screens
10762  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10763  * @cfg {string} value default value of the input
10764  * @cfg {Number} labelWidth set the width of label 
10765  * @cfg {Number} labellg set the width of label (1-12)
10766  * @cfg {Number} labelmd set the width of label (1-12)
10767  * @cfg {Number} labelsm set the width of label (1-12)
10768  * @cfg {Number} labelxs set the width of label (1-12)
10769  * @cfg {String} labelAlign (top|left)
10770  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10771  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10772  * @cfg {String} indicatorpos (left|right) default left
10773  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10774  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10775  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10776
10777  * @cfg {String} align (left|center|right) Default left
10778  * @cfg {Boolean} forceFeedback (true|false) Default false
10779  * 
10780  * @constructor
10781  * Create a new Input
10782  * @param {Object} config The config object
10783  */
10784
10785 Roo.bootstrap.Input = function(config){
10786     
10787     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10788     
10789     this.addEvents({
10790         /**
10791          * @event focus
10792          * Fires when this field receives input focus.
10793          * @param {Roo.form.Field} this
10794          */
10795         focus : true,
10796         /**
10797          * @event blur
10798          * Fires when this field loses input focus.
10799          * @param {Roo.form.Field} this
10800          */
10801         blur : true,
10802         /**
10803          * @event specialkey
10804          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10805          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10806          * @param {Roo.form.Field} this
10807          * @param {Roo.EventObject} e The event object
10808          */
10809         specialkey : true,
10810         /**
10811          * @event change
10812          * Fires just before the field blurs if the field value has changed.
10813          * @param {Roo.form.Field} this
10814          * @param {Mixed} newValue The new value
10815          * @param {Mixed} oldValue The original value
10816          */
10817         change : true,
10818         /**
10819          * @event invalid
10820          * Fires after the field has been marked as invalid.
10821          * @param {Roo.form.Field} this
10822          * @param {String} msg The validation message
10823          */
10824         invalid : true,
10825         /**
10826          * @event valid
10827          * Fires after the field has been validated with no errors.
10828          * @param {Roo.form.Field} this
10829          */
10830         valid : true,
10831          /**
10832          * @event keyup
10833          * Fires after the key up
10834          * @param {Roo.form.Field} this
10835          * @param {Roo.EventObject}  e The event Object
10836          */
10837         keyup : true,
10838         /**
10839          * @event paste
10840          * Fires after the user pastes into input
10841          * @param {Roo.form.Field} this
10842          * @param {Roo.EventObject}  e The event Object
10843          */
10844         paste : true
10845     });
10846 };
10847
10848 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10849      /**
10850      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10851       automatic validation (defaults to "keyup").
10852      */
10853     validationEvent : "keyup",
10854      /**
10855      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10856      */
10857     validateOnBlur : true,
10858     /**
10859      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10860      */
10861     validationDelay : 250,
10862      /**
10863      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10864      */
10865     focusClass : "x-form-focus",  // not needed???
10866     
10867        
10868     /**
10869      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10870      */
10871     invalidClass : "has-warning",
10872     
10873     /**
10874      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10875      */
10876     validClass : "has-success",
10877     
10878     /**
10879      * @cfg {Boolean} hasFeedback (true|false) default true
10880      */
10881     hasFeedback : true,
10882     
10883     /**
10884      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10885      */
10886     invalidFeedbackClass : "glyphicon-warning-sign",
10887     
10888     /**
10889      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10890      */
10891     validFeedbackClass : "glyphicon-ok",
10892     
10893     /**
10894      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10895      */
10896     selectOnFocus : false,
10897     
10898      /**
10899      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10900      */
10901     maskRe : null,
10902        /**
10903      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10904      */
10905     vtype : null,
10906     
10907       /**
10908      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10909      */
10910     disableKeyFilter : false,
10911     
10912        /**
10913      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10914      */
10915     disabled : false,
10916      /**
10917      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10918      */
10919     allowBlank : true,
10920     /**
10921      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10922      */
10923     blankText : "Please complete this mandatory field",
10924     
10925      /**
10926      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10927      */
10928     minLength : 0,
10929     /**
10930      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10931      */
10932     maxLength : Number.MAX_VALUE,
10933     /**
10934      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10935      */
10936     minLengthText : "The minimum length for this field is {0}",
10937     /**
10938      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10939      */
10940     maxLengthText : "The maximum length for this field is {0}",
10941   
10942     
10943     /**
10944      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10945      * If available, this function will be called only after the basic validators all return true, and will be passed the
10946      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10947      */
10948     validator : null,
10949     /**
10950      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10951      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10952      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10953      */
10954     regex : null,
10955     /**
10956      * @cfg {String} regexText -- Depricated - use Invalid Text
10957      */
10958     regexText : "",
10959     
10960     /**
10961      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10962      */
10963     invalidText : "",
10964     
10965     
10966     
10967     autocomplete: false,
10968     
10969     
10970     fieldLabel : '',
10971     inputType : 'text',
10972     
10973     name : false,
10974     placeholder: false,
10975     before : false,
10976     after : false,
10977     size : false,
10978     hasFocus : false,
10979     preventMark: false,
10980     isFormField : true,
10981     value : '',
10982     labelWidth : 2,
10983     labelAlign : false,
10984     readOnly : false,
10985     align : false,
10986     formatedValue : false,
10987     forceFeedback : false,
10988     
10989     indicatorpos : 'left',
10990     
10991     labellg : 0,
10992     labelmd : 0,
10993     labelsm : 0,
10994     labelxs : 0,
10995     
10996     capture : '',
10997     accept : '',
10998     
10999     parentLabelAlign : function()
11000     {
11001         var parent = this;
11002         while (parent.parent()) {
11003             parent = parent.parent();
11004             if (typeof(parent.labelAlign) !='undefined') {
11005                 return parent.labelAlign;
11006             }
11007         }
11008         return 'left';
11009         
11010     },
11011     
11012     getAutoCreate : function()
11013     {
11014         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11015         
11016         var id = Roo.id();
11017         
11018         var cfg = {};
11019         
11020         if(this.inputType != 'hidden'){
11021             cfg.cls = 'form-group' //input-group
11022         }
11023         
11024         var input =  {
11025             tag: 'input',
11026             id : id,
11027             type : this.inputType,
11028             value : this.value,
11029             cls : 'form-control',
11030             placeholder : this.placeholder || '',
11031             autocomplete : this.autocomplete || 'new-password'
11032         };
11033         if (this.inputType == 'file') {
11034             input.style = 'overflow:hidden'; // why not in CSS?
11035         }
11036         
11037         if(this.capture.length){
11038             input.capture = this.capture;
11039         }
11040         
11041         if(this.accept.length){
11042             input.accept = this.accept + "/*";
11043         }
11044         
11045         if(this.align){
11046             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11047         }
11048         
11049         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11050             input.maxLength = this.maxLength;
11051         }
11052         
11053         if (this.disabled) {
11054             input.disabled=true;
11055         }
11056         
11057         if (this.readOnly) {
11058             input.readonly=true;
11059         }
11060         
11061         if (this.name) {
11062             input.name = this.name;
11063         }
11064         
11065         if (this.size) {
11066             input.cls += ' input-' + this.size;
11067         }
11068         
11069         var settings=this;
11070         ['xs','sm','md','lg'].map(function(size){
11071             if (settings[size]) {
11072                 cfg.cls += ' col-' + size + '-' + settings[size];
11073             }
11074         });
11075         
11076         var inputblock = input;
11077         
11078         var feedback = {
11079             tag: 'span',
11080             cls: 'glyphicon form-control-feedback'
11081         };
11082             
11083         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11084             
11085             inputblock = {
11086                 cls : 'has-feedback',
11087                 cn :  [
11088                     input,
11089                     feedback
11090                 ] 
11091             };  
11092         }
11093         
11094         if (this.before || this.after) {
11095             
11096             inputblock = {
11097                 cls : 'input-group',
11098                 cn :  [] 
11099             };
11100             
11101             if (this.before && typeof(this.before) == 'string') {
11102                 
11103                 inputblock.cn.push({
11104                     tag :'span',
11105                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11106                     html : this.before
11107                 });
11108             }
11109             if (this.before && typeof(this.before) == 'object') {
11110                 this.before = Roo.factory(this.before);
11111                 
11112                 inputblock.cn.push({
11113                     tag :'span',
11114                     cls : 'roo-input-before input-group-prepend   input-group-' +
11115                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11116                 });
11117             }
11118             
11119             inputblock.cn.push(input);
11120             
11121             if (this.after && typeof(this.after) == 'string') {
11122                 inputblock.cn.push({
11123                     tag :'span',
11124                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11125                     html : this.after
11126                 });
11127             }
11128             if (this.after && typeof(this.after) == 'object') {
11129                 this.after = Roo.factory(this.after);
11130                 
11131                 inputblock.cn.push({
11132                     tag :'span',
11133                     cls : 'roo-input-after input-group-append  input-group-' +
11134                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11135                 });
11136             }
11137             
11138             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11139                 inputblock.cls += ' has-feedback';
11140                 inputblock.cn.push(feedback);
11141             }
11142         };
11143         var indicator = {
11144             tag : 'i',
11145             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11146             tooltip : 'This field is required'
11147         };
11148         if (this.allowBlank ) {
11149             indicator.style = this.allowBlank ? ' display:none' : '';
11150         }
11151         if (align ==='left' && this.fieldLabel.length) {
11152             
11153             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11154             
11155             cfg.cn = [
11156                 indicator,
11157                 {
11158                     tag: 'label',
11159                     'for' :  id,
11160                     cls : 'control-label col-form-label',
11161                     html : this.fieldLabel
11162
11163                 },
11164                 {
11165                     cls : "", 
11166                     cn: [
11167                         inputblock
11168                     ]
11169                 }
11170             ];
11171             
11172             var labelCfg = cfg.cn[1];
11173             var contentCfg = cfg.cn[2];
11174             
11175             if(this.indicatorpos == 'right'){
11176                 cfg.cn = [
11177                     {
11178                         tag: 'label',
11179                         'for' :  id,
11180                         cls : 'control-label col-form-label',
11181                         cn : [
11182                             {
11183                                 tag : 'span',
11184                                 html : this.fieldLabel
11185                             },
11186                             indicator
11187                         ]
11188                     },
11189                     {
11190                         cls : "",
11191                         cn: [
11192                             inputblock
11193                         ]
11194                     }
11195
11196                 ];
11197                 
11198                 labelCfg = cfg.cn[0];
11199                 contentCfg = cfg.cn[1];
11200             
11201             }
11202             
11203             if(this.labelWidth > 12){
11204                 labelCfg.style = "width: " + this.labelWidth + 'px';
11205             }
11206             
11207             if(this.labelWidth < 13 && this.labelmd == 0){
11208                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11209             }
11210             
11211             if(this.labellg > 0){
11212                 labelCfg.cls += ' col-lg-' + this.labellg;
11213                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11214             }
11215             
11216             if(this.labelmd > 0){
11217                 labelCfg.cls += ' col-md-' + this.labelmd;
11218                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11219             }
11220             
11221             if(this.labelsm > 0){
11222                 labelCfg.cls += ' col-sm-' + this.labelsm;
11223                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11224             }
11225             
11226             if(this.labelxs > 0){
11227                 labelCfg.cls += ' col-xs-' + this.labelxs;
11228                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11229             }
11230             
11231             
11232         } else if ( this.fieldLabel.length) {
11233                 
11234             
11235             
11236             cfg.cn = [
11237                 {
11238                     tag : 'i',
11239                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11240                     tooltip : 'This field is required',
11241                     style : this.allowBlank ? ' display:none' : '' 
11242                 },
11243                 {
11244                     tag: 'label',
11245                    //cls : 'input-group-addon',
11246                     html : this.fieldLabel
11247
11248                 },
11249
11250                inputblock
11251
11252            ];
11253            
11254            if(this.indicatorpos == 'right'){
11255        
11256                 cfg.cn = [
11257                     {
11258                         tag: 'label',
11259                        //cls : 'input-group-addon',
11260                         html : this.fieldLabel
11261
11262                     },
11263                     {
11264                         tag : 'i',
11265                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11266                         tooltip : 'This field is required',
11267                         style : this.allowBlank ? ' display:none' : '' 
11268                     },
11269
11270                    inputblock
11271
11272                ];
11273
11274             }
11275
11276         } else {
11277             
11278             cfg.cn = [
11279
11280                     inputblock
11281
11282             ];
11283                 
11284                 
11285         };
11286         
11287         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11288            cfg.cls += ' navbar-form';
11289         }
11290         
11291         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11292             // on BS4 we do this only if not form 
11293             cfg.cls += ' navbar-form';
11294             cfg.tag = 'li';
11295         }
11296         
11297         return cfg;
11298         
11299     },
11300     /**
11301      * return the real input element.
11302      */
11303     inputEl: function ()
11304     {
11305         return this.el.select('input.form-control',true).first();
11306     },
11307     
11308     tooltipEl : function()
11309     {
11310         return this.inputEl();
11311     },
11312     
11313     indicatorEl : function()
11314     {
11315         if (Roo.bootstrap.version == 4) {
11316             return false; // not enabled in v4 yet.
11317         }
11318         
11319         var indicator = this.el.select('i.roo-required-indicator',true).first();
11320         
11321         if(!indicator){
11322             return false;
11323         }
11324         
11325         return indicator;
11326         
11327     },
11328     
11329     setDisabled : function(v)
11330     {
11331         var i  = this.inputEl().dom;
11332         if (!v) {
11333             i.removeAttribute('disabled');
11334             return;
11335             
11336         }
11337         i.setAttribute('disabled','true');
11338     },
11339     initEvents : function()
11340     {
11341           
11342         this.inputEl().on("keydown" , this.fireKey,  this);
11343         this.inputEl().on("focus", this.onFocus,  this);
11344         this.inputEl().on("blur", this.onBlur,  this);
11345         
11346         this.inputEl().relayEvent('keyup', this);
11347         this.inputEl().relayEvent('paste', this);
11348         
11349         this.indicator = this.indicatorEl();
11350         
11351         if(this.indicator){
11352             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11353         }
11354  
11355         // reference to original value for reset
11356         this.originalValue = this.getValue();
11357         //Roo.form.TextField.superclass.initEvents.call(this);
11358         if(this.validationEvent == 'keyup'){
11359             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11360             this.inputEl().on('keyup', this.filterValidation, this);
11361         }
11362         else if(this.validationEvent !== false){
11363             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11364         }
11365         
11366         if(this.selectOnFocus){
11367             this.on("focus", this.preFocus, this);
11368             
11369         }
11370         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11371             this.inputEl().on("keypress", this.filterKeys, this);
11372         } else {
11373             this.inputEl().relayEvent('keypress', this);
11374         }
11375        /* if(this.grow){
11376             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11377             this.el.on("click", this.autoSize,  this);
11378         }
11379         */
11380         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11381             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11382         }
11383         
11384         if (typeof(this.before) == 'object') {
11385             this.before.render(this.el.select('.roo-input-before',true).first());
11386         }
11387         if (typeof(this.after) == 'object') {
11388             this.after.render(this.el.select('.roo-input-after',true).first());
11389         }
11390         
11391         this.inputEl().on('change', this.onChange, this);
11392         
11393     },
11394     filterValidation : function(e){
11395         if(!e.isNavKeyPress()){
11396             this.validationTask.delay(this.validationDelay);
11397         }
11398     },
11399      /**
11400      * Validates the field value
11401      * @return {Boolean} True if the value is valid, else false
11402      */
11403     validate : function(){
11404         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11405         if(this.disabled || this.validateValue(this.getRawValue())){
11406             this.markValid();
11407             return true;
11408         }
11409         
11410         this.markInvalid();
11411         return false;
11412     },
11413     
11414     
11415     /**
11416      * Validates a value according to the field's validation rules and marks the field as invalid
11417      * if the validation fails
11418      * @param {Mixed} value The value to validate
11419      * @return {Boolean} True if the value is valid, else false
11420      */
11421     validateValue : function(value)
11422     {
11423         if(this.getVisibilityEl().hasClass('hidden')){
11424             return true;
11425         }
11426         
11427         if(value.length < 1)  { // if it's blank
11428             if(this.allowBlank){
11429                 return true;
11430             }
11431             return false;
11432         }
11433         
11434         if(value.length < this.minLength){
11435             return false;
11436         }
11437         if(value.length > this.maxLength){
11438             return false;
11439         }
11440         if(this.vtype){
11441             var vt = Roo.form.VTypes;
11442             if(!vt[this.vtype](value, this)){
11443                 return false;
11444             }
11445         }
11446         if(typeof this.validator == "function"){
11447             var msg = this.validator(value);
11448             if(msg !== true){
11449                 return false;
11450             }
11451             if (typeof(msg) == 'string') {
11452                 this.invalidText = msg;
11453             }
11454         }
11455         
11456         if(this.regex && !this.regex.test(value)){
11457             return false;
11458         }
11459         
11460         return true;
11461     },
11462     
11463      // private
11464     fireKey : function(e){
11465         //Roo.log('field ' + e.getKey());
11466         if(e.isNavKeyPress()){
11467             this.fireEvent("specialkey", this, e);
11468         }
11469     },
11470     focus : function (selectText){
11471         if(this.rendered){
11472             this.inputEl().focus();
11473             if(selectText === true){
11474                 this.inputEl().dom.select();
11475             }
11476         }
11477         return this;
11478     } ,
11479     
11480     onFocus : function(){
11481         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11482            // this.el.addClass(this.focusClass);
11483         }
11484         if(!this.hasFocus){
11485             this.hasFocus = true;
11486             this.startValue = this.getValue();
11487             this.fireEvent("focus", this);
11488         }
11489     },
11490     
11491     beforeBlur : Roo.emptyFn,
11492
11493     
11494     // private
11495     onBlur : function(){
11496         this.beforeBlur();
11497         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11498             //this.el.removeClass(this.focusClass);
11499         }
11500         this.hasFocus = false;
11501         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11502             this.validate();
11503         }
11504         var v = this.getValue();
11505         if(String(v) !== String(this.startValue)){
11506             this.fireEvent('change', this, v, this.startValue);
11507         }
11508         this.fireEvent("blur", this);
11509     },
11510     
11511     onChange : function(e)
11512     {
11513         var v = this.getValue();
11514         if(String(v) !== String(this.startValue)){
11515             this.fireEvent('change', this, v, this.startValue);
11516         }
11517         
11518     },
11519     
11520     /**
11521      * Resets the current field value to the originally loaded value and clears any validation messages
11522      */
11523     reset : function(){
11524         this.setValue(this.originalValue);
11525         this.validate();
11526     },
11527      /**
11528      * Returns the name of the field
11529      * @return {Mixed} name The name field
11530      */
11531     getName: function(){
11532         return this.name;
11533     },
11534      /**
11535      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11536      * @return {Mixed} value The field value
11537      */
11538     getValue : function(){
11539         
11540         var v = this.inputEl().getValue();
11541         
11542         return v;
11543     },
11544     /**
11545      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11546      * @return {Mixed} value The field value
11547      */
11548     getRawValue : function(){
11549         var v = this.inputEl().getValue();
11550         
11551         return v;
11552     },
11553     
11554     /**
11555      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11556      * @param {Mixed} value The value to set
11557      */
11558     setRawValue : function(v){
11559         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11560     },
11561     
11562     selectText : function(start, end){
11563         var v = this.getRawValue();
11564         if(v.length > 0){
11565             start = start === undefined ? 0 : start;
11566             end = end === undefined ? v.length : end;
11567             var d = this.inputEl().dom;
11568             if(d.setSelectionRange){
11569                 d.setSelectionRange(start, end);
11570             }else if(d.createTextRange){
11571                 var range = d.createTextRange();
11572                 range.moveStart("character", start);
11573                 range.moveEnd("character", v.length-end);
11574                 range.select();
11575             }
11576         }
11577     },
11578     
11579     /**
11580      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11581      * @param {Mixed} value The value to set
11582      */
11583     setValue : function(v){
11584         this.value = v;
11585         if(this.rendered){
11586             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11587             this.validate();
11588         }
11589     },
11590     
11591     /*
11592     processValue : function(value){
11593         if(this.stripCharsRe){
11594             var newValue = value.replace(this.stripCharsRe, '');
11595             if(newValue !== value){
11596                 this.setRawValue(newValue);
11597                 return newValue;
11598             }
11599         }
11600         return value;
11601     },
11602   */
11603     preFocus : function(){
11604         
11605         if(this.selectOnFocus){
11606             this.inputEl().dom.select();
11607         }
11608     },
11609     filterKeys : function(e){
11610         var k = e.getKey();
11611         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11612             return;
11613         }
11614         var c = e.getCharCode(), cc = String.fromCharCode(c);
11615         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11616             return;
11617         }
11618         if(!this.maskRe.test(cc)){
11619             e.stopEvent();
11620         }
11621     },
11622      /**
11623      * Clear any invalid styles/messages for this field
11624      */
11625     clearInvalid : function(){
11626         
11627         if(!this.el || this.preventMark){ // not rendered
11628             return;
11629         }
11630         
11631         
11632         this.el.removeClass([this.invalidClass, 'is-invalid']);
11633         
11634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11635             
11636             var feedback = this.el.select('.form-control-feedback', true).first();
11637             
11638             if(feedback){
11639                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11640             }
11641             
11642         }
11643         
11644         if(this.indicator){
11645             this.indicator.removeClass('visible');
11646             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11647         }
11648         
11649         this.fireEvent('valid', this);
11650     },
11651     
11652      /**
11653      * Mark this field as valid
11654      */
11655     markValid : function()
11656     {
11657         if(!this.el  || this.preventMark){ // not rendered...
11658             return;
11659         }
11660         
11661         this.el.removeClass([this.invalidClass, this.validClass]);
11662         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11663
11664         var feedback = this.el.select('.form-control-feedback', true).first();
11665             
11666         if(feedback){
11667             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11668         }
11669         
11670         if(this.indicator){
11671             this.indicator.removeClass('visible');
11672             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11673         }
11674         
11675         if(this.disabled){
11676             return;
11677         }
11678         
11679            
11680         if(this.allowBlank && !this.getRawValue().length){
11681             return;
11682         }
11683         if (Roo.bootstrap.version == 3) {
11684             this.el.addClass(this.validClass);
11685         } else {
11686             this.inputEl().addClass('is-valid');
11687         }
11688
11689         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11690             
11691             var feedback = this.el.select('.form-control-feedback', true).first();
11692             
11693             if(feedback){
11694                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11695                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11696             }
11697             
11698         }
11699         
11700         this.fireEvent('valid', this);
11701     },
11702     
11703      /**
11704      * Mark this field as invalid
11705      * @param {String} msg The validation message
11706      */
11707     markInvalid : function(msg)
11708     {
11709         if(!this.el  || this.preventMark){ // not rendered
11710             return;
11711         }
11712         
11713         this.el.removeClass([this.invalidClass, this.validClass]);
11714         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11715         
11716         var feedback = this.el.select('.form-control-feedback', true).first();
11717             
11718         if(feedback){
11719             this.el.select('.form-control-feedback', true).first().removeClass(
11720                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11721         }
11722
11723         if(this.disabled){
11724             return;
11725         }
11726         
11727         if(this.allowBlank && !this.getRawValue().length){
11728             return;
11729         }
11730         
11731         if(this.indicator){
11732             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11733             this.indicator.addClass('visible');
11734         }
11735         if (Roo.bootstrap.version == 3) {
11736             this.el.addClass(this.invalidClass);
11737         } else {
11738             this.inputEl().addClass('is-invalid');
11739         }
11740         
11741         
11742         
11743         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11744             
11745             var feedback = this.el.select('.form-control-feedback', true).first();
11746             
11747             if(feedback){
11748                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11749                 
11750                 if(this.getValue().length || this.forceFeedback){
11751                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11752                 }
11753                 
11754             }
11755             
11756         }
11757         
11758         this.fireEvent('invalid', this, msg);
11759     },
11760     // private
11761     SafariOnKeyDown : function(event)
11762     {
11763         // this is a workaround for a password hang bug on chrome/ webkit.
11764         if (this.inputEl().dom.type != 'password') {
11765             return;
11766         }
11767         
11768         var isSelectAll = false;
11769         
11770         if(this.inputEl().dom.selectionEnd > 0){
11771             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11772         }
11773         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11774             event.preventDefault();
11775             this.setValue('');
11776             return;
11777         }
11778         
11779         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11780             
11781             event.preventDefault();
11782             // this is very hacky as keydown always get's upper case.
11783             //
11784             var cc = String.fromCharCode(event.getCharCode());
11785             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11786             
11787         }
11788     },
11789     adjustWidth : function(tag, w){
11790         tag = tag.toLowerCase();
11791         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11792             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11793                 if(tag == 'input'){
11794                     return w + 2;
11795                 }
11796                 if(tag == 'textarea'){
11797                     return w-2;
11798                 }
11799             }else if(Roo.isOpera){
11800                 if(tag == 'input'){
11801                     return w + 2;
11802                 }
11803                 if(tag == 'textarea'){
11804                     return w-2;
11805                 }
11806             }
11807         }
11808         return w;
11809     },
11810     
11811     setFieldLabel : function(v)
11812     {
11813         if(!this.rendered){
11814             return;
11815         }
11816         
11817         if(this.indicatorEl()){
11818             var ar = this.el.select('label > span',true);
11819             
11820             if (ar.elements.length) {
11821                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11822                 this.fieldLabel = v;
11823                 return;
11824             }
11825             
11826             var br = this.el.select('label',true);
11827             
11828             if(br.elements.length) {
11829                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11830                 this.fieldLabel = v;
11831                 return;
11832             }
11833             
11834             Roo.log('Cannot Found any of label > span || label in input');
11835             return;
11836         }
11837         
11838         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11839         this.fieldLabel = v;
11840         
11841         
11842     }
11843 });
11844
11845  
11846 /*
11847  * - LGPL
11848  *
11849  * Input
11850  * 
11851  */
11852
11853 /**
11854  * @class Roo.bootstrap.TextArea
11855  * @extends Roo.bootstrap.Input
11856  * Bootstrap TextArea class
11857  * @cfg {Number} cols Specifies the visible width of a text area
11858  * @cfg {Number} rows Specifies the visible number of lines in a text area
11859  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11860  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11861  * @cfg {string} html text
11862  * 
11863  * @constructor
11864  * Create a new TextArea
11865  * @param {Object} config The config object
11866  */
11867
11868 Roo.bootstrap.TextArea = function(config){
11869     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11870    
11871 };
11872
11873 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11874      
11875     cols : false,
11876     rows : 5,
11877     readOnly : false,
11878     warp : 'soft',
11879     resize : false,
11880     value: false,
11881     html: false,
11882     
11883     getAutoCreate : function(){
11884         
11885         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11886         
11887         var id = Roo.id();
11888         
11889         var cfg = {};
11890         
11891         if(this.inputType != 'hidden'){
11892             cfg.cls = 'form-group' //input-group
11893         }
11894         
11895         var input =  {
11896             tag: 'textarea',
11897             id : id,
11898             warp : this.warp,
11899             rows : this.rows,
11900             value : this.value || '',
11901             html: this.html || '',
11902             cls : 'form-control',
11903             placeholder : this.placeholder || '' 
11904             
11905         };
11906         
11907         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11908             input.maxLength = this.maxLength;
11909         }
11910         
11911         if(this.resize){
11912             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11913         }
11914         
11915         if(this.cols){
11916             input.cols = this.cols;
11917         }
11918         
11919         if (this.readOnly) {
11920             input.readonly = true;
11921         }
11922         
11923         if (this.name) {
11924             input.name = this.name;
11925         }
11926         
11927         if (this.size) {
11928             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11929         }
11930         
11931         var settings=this;
11932         ['xs','sm','md','lg'].map(function(size){
11933             if (settings[size]) {
11934                 cfg.cls += ' col-' + size + '-' + settings[size];
11935             }
11936         });
11937         
11938         var inputblock = input;
11939         
11940         if(this.hasFeedback && !this.allowBlank){
11941             
11942             var feedback = {
11943                 tag: 'span',
11944                 cls: 'glyphicon form-control-feedback'
11945             };
11946
11947             inputblock = {
11948                 cls : 'has-feedback',
11949                 cn :  [
11950                     input,
11951                     feedback
11952                 ] 
11953             };  
11954         }
11955         
11956         
11957         if (this.before || this.after) {
11958             
11959             inputblock = {
11960                 cls : 'input-group',
11961                 cn :  [] 
11962             };
11963             if (this.before) {
11964                 inputblock.cn.push({
11965                     tag :'span',
11966                     cls : 'input-group-addon',
11967                     html : this.before
11968                 });
11969             }
11970             
11971             inputblock.cn.push(input);
11972             
11973             if(this.hasFeedback && !this.allowBlank){
11974                 inputblock.cls += ' has-feedback';
11975                 inputblock.cn.push(feedback);
11976             }
11977             
11978             if (this.after) {
11979                 inputblock.cn.push({
11980                     tag :'span',
11981                     cls : 'input-group-addon',
11982                     html : this.after
11983                 });
11984             }
11985             
11986         }
11987         
11988         if (align ==='left' && this.fieldLabel.length) {
11989             cfg.cn = [
11990                 {
11991                     tag: 'label',
11992                     'for' :  id,
11993                     cls : 'control-label',
11994                     html : this.fieldLabel
11995                 },
11996                 {
11997                     cls : "",
11998                     cn: [
11999                         inputblock
12000                     ]
12001                 }
12002
12003             ];
12004             
12005             if(this.labelWidth > 12){
12006                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12007             }
12008
12009             if(this.labelWidth < 13 && this.labelmd == 0){
12010                 this.labelmd = this.labelWidth;
12011             }
12012
12013             if(this.labellg > 0){
12014                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12015                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12016             }
12017
12018             if(this.labelmd > 0){
12019                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12020                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12021             }
12022
12023             if(this.labelsm > 0){
12024                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12025                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12026             }
12027
12028             if(this.labelxs > 0){
12029                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12030                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12031             }
12032             
12033         } else if ( this.fieldLabel.length) {
12034             cfg.cn = [
12035
12036                {
12037                    tag: 'label',
12038                    //cls : 'input-group-addon',
12039                    html : this.fieldLabel
12040
12041                },
12042
12043                inputblock
12044
12045            ];
12046
12047         } else {
12048
12049             cfg.cn = [
12050
12051                 inputblock
12052
12053             ];
12054                 
12055         }
12056         
12057         if (this.disabled) {
12058             input.disabled=true;
12059         }
12060         
12061         return cfg;
12062         
12063     },
12064     /**
12065      * return the real textarea element.
12066      */
12067     inputEl: function ()
12068     {
12069         return this.el.select('textarea.form-control',true).first();
12070     },
12071     
12072     /**
12073      * Clear any invalid styles/messages for this field
12074      */
12075     clearInvalid : function()
12076     {
12077         
12078         if(!this.el || this.preventMark){ // not rendered
12079             return;
12080         }
12081         
12082         var label = this.el.select('label', true).first();
12083         var icon = this.el.select('i.fa-star', true).first();
12084         
12085         if(label && icon){
12086             icon.remove();
12087         }
12088         this.el.removeClass( this.validClass);
12089         this.inputEl().removeClass('is-invalid');
12090          
12091         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12092             
12093             var feedback = this.el.select('.form-control-feedback', true).first();
12094             
12095             if(feedback){
12096                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12097             }
12098             
12099         }
12100         
12101         this.fireEvent('valid', this);
12102     },
12103     
12104      /**
12105      * Mark this field as valid
12106      */
12107     markValid : function()
12108     {
12109         if(!this.el  || this.preventMark){ // not rendered
12110             return;
12111         }
12112         
12113         this.el.removeClass([this.invalidClass, this.validClass]);
12114         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12115         
12116         var feedback = this.el.select('.form-control-feedback', true).first();
12117             
12118         if(feedback){
12119             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12120         }
12121
12122         if(this.disabled || this.allowBlank){
12123             return;
12124         }
12125         
12126         var label = this.el.select('label', true).first();
12127         var icon = this.el.select('i.fa-star', true).first();
12128         
12129         if(label && icon){
12130             icon.remove();
12131         }
12132         if (Roo.bootstrap.version == 3) {
12133             this.el.addClass(this.validClass);
12134         } else {
12135             this.inputEl().addClass('is-valid');
12136         }
12137         
12138         
12139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12140             
12141             var feedback = this.el.select('.form-control-feedback', true).first();
12142             
12143             if(feedback){
12144                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12145                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12146             }
12147             
12148         }
12149         
12150         this.fireEvent('valid', this);
12151     },
12152     
12153      /**
12154      * Mark this field as invalid
12155      * @param {String} msg The validation message
12156      */
12157     markInvalid : function(msg)
12158     {
12159         if(!this.el  || this.preventMark){ // not rendered
12160             return;
12161         }
12162         
12163         this.el.removeClass([this.invalidClass, this.validClass]);
12164         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12165         
12166         var feedback = this.el.select('.form-control-feedback', true).first();
12167             
12168         if(feedback){
12169             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12170         }
12171
12172         if(this.disabled || this.allowBlank){
12173             return;
12174         }
12175         
12176         var label = this.el.select('label', true).first();
12177         var icon = this.el.select('i.fa-star', true).first();
12178         
12179         if(!this.getValue().length && label && !icon){
12180             this.el.createChild({
12181                 tag : 'i',
12182                 cls : 'text-danger fa fa-lg fa-star',
12183                 tooltip : 'This field is required',
12184                 style : 'margin-right:5px;'
12185             }, label, true);
12186         }
12187         
12188         if (Roo.bootstrap.version == 3) {
12189             this.el.addClass(this.invalidClass);
12190         } else {
12191             this.inputEl().addClass('is-invalid');
12192         }
12193         
12194         // fixme ... this may be depricated need to test..
12195         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12196             
12197             var feedback = this.el.select('.form-control-feedback', true).first();
12198             
12199             if(feedback){
12200                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12201                 
12202                 if(this.getValue().length || this.forceFeedback){
12203                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12204                 }
12205                 
12206             }
12207             
12208         }
12209         
12210         this.fireEvent('invalid', this, msg);
12211     }
12212 });
12213
12214  
12215 /*
12216  * - LGPL
12217  *
12218  * trigger field - base class for combo..
12219  * 
12220  */
12221  
12222 /**
12223  * @class Roo.bootstrap.TriggerField
12224  * @extends Roo.bootstrap.Input
12225  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12226  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12227  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12228  * for which you can provide a custom implementation.  For example:
12229  * <pre><code>
12230 var trigger = new Roo.bootstrap.TriggerField();
12231 trigger.onTriggerClick = myTriggerFn;
12232 trigger.applyTo('my-field');
12233 </code></pre>
12234  *
12235  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12236  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12237  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12238  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12239  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12240
12241  * @constructor
12242  * Create a new TriggerField.
12243  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12244  * to the base TextField)
12245  */
12246 Roo.bootstrap.TriggerField = function(config){
12247     this.mimicing = false;
12248     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12249 };
12250
12251 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12252     /**
12253      * @cfg {String} triggerClass A CSS class to apply to the trigger
12254      */
12255      /**
12256      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12257      */
12258     hideTrigger:false,
12259
12260     /**
12261      * @cfg {Boolean} removable (true|false) special filter default false
12262      */
12263     removable : false,
12264     
12265     /** @cfg {Boolean} grow @hide */
12266     /** @cfg {Number} growMin @hide */
12267     /** @cfg {Number} growMax @hide */
12268
12269     /**
12270      * @hide 
12271      * @method
12272      */
12273     autoSize: Roo.emptyFn,
12274     // private
12275     monitorTab : true,
12276     // private
12277     deferHeight : true,
12278
12279     
12280     actionMode : 'wrap',
12281     
12282     caret : false,
12283     
12284     
12285     getAutoCreate : function(){
12286        
12287         var align = this.labelAlign || this.parentLabelAlign();
12288         
12289         var id = Roo.id();
12290         
12291         var cfg = {
12292             cls: 'form-group' //input-group
12293         };
12294         
12295         
12296         var input =  {
12297             tag: 'input',
12298             id : id,
12299             type : this.inputType,
12300             cls : 'form-control',
12301             autocomplete: 'new-password',
12302             placeholder : this.placeholder || '' 
12303             
12304         };
12305         if (this.name) {
12306             input.name = this.name;
12307         }
12308         if (this.size) {
12309             input.cls += ' input-' + this.size;
12310         }
12311         
12312         if (this.disabled) {
12313             input.disabled=true;
12314         }
12315         
12316         var inputblock = input;
12317         
12318         if(this.hasFeedback && !this.allowBlank){
12319             
12320             var feedback = {
12321                 tag: 'span',
12322                 cls: 'glyphicon form-control-feedback'
12323             };
12324             
12325             if(this.removable && !this.editable  ){
12326                 inputblock = {
12327                     cls : 'has-feedback',
12328                     cn :  [
12329                         inputblock,
12330                         {
12331                             tag: 'button',
12332                             html : 'x',
12333                             cls : 'roo-combo-removable-btn close'
12334                         },
12335                         feedback
12336                     ] 
12337                 };
12338             } else {
12339                 inputblock = {
12340                     cls : 'has-feedback',
12341                     cn :  [
12342                         inputblock,
12343                         feedback
12344                     ] 
12345                 };
12346             }
12347
12348         } else {
12349             if(this.removable && !this.editable ){
12350                 inputblock = {
12351                     cls : 'roo-removable',
12352                     cn :  [
12353                         inputblock,
12354                         {
12355                             tag: 'button',
12356                             html : 'x',
12357                             cls : 'roo-combo-removable-btn close'
12358                         }
12359                     ] 
12360                 };
12361             }
12362         }
12363         
12364         if (this.before || this.after) {
12365             
12366             inputblock = {
12367                 cls : 'input-group',
12368                 cn :  [] 
12369             };
12370             if (this.before) {
12371                 inputblock.cn.push({
12372                     tag :'span',
12373                     cls : 'input-group-addon input-group-prepend input-group-text',
12374                     html : this.before
12375                 });
12376             }
12377             
12378             inputblock.cn.push(input);
12379             
12380             if(this.hasFeedback && !this.allowBlank){
12381                 inputblock.cls += ' has-feedback';
12382                 inputblock.cn.push(feedback);
12383             }
12384             
12385             if (this.after) {
12386                 inputblock.cn.push({
12387                     tag :'span',
12388                     cls : 'input-group-addon input-group-append input-group-text',
12389                     html : this.after
12390                 });
12391             }
12392             
12393         };
12394         
12395       
12396         
12397         var ibwrap = inputblock;
12398         
12399         if(this.multiple){
12400             ibwrap = {
12401                 tag: 'ul',
12402                 cls: 'roo-select2-choices',
12403                 cn:[
12404                     {
12405                         tag: 'li',
12406                         cls: 'roo-select2-search-field',
12407                         cn: [
12408
12409                             inputblock
12410                         ]
12411                     }
12412                 ]
12413             };
12414                 
12415         }
12416         
12417         var combobox = {
12418             cls: 'roo-select2-container input-group',
12419             cn: [
12420                  {
12421                     tag: 'input',
12422                     type : 'hidden',
12423                     cls: 'form-hidden-field'
12424                 },
12425                 ibwrap
12426             ]
12427         };
12428         
12429         if(!this.multiple && this.showToggleBtn){
12430             
12431             var caret = {
12432                         tag: 'span',
12433                         cls: 'caret'
12434              };
12435             if (this.caret != false) {
12436                 caret = {
12437                      tag: 'i',
12438                      cls: 'fa fa-' + this.caret
12439                 };
12440                 
12441             }
12442             
12443             combobox.cn.push({
12444                 tag :'span',
12445                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12446                 cn : [
12447                     Roo.bootstrap.version == 3 ? caret : '',
12448                     {
12449                         tag: 'span',
12450                         cls: 'combobox-clear',
12451                         cn  : [
12452                             {
12453                                 tag : 'i',
12454                                 cls: 'icon-remove'
12455                             }
12456                         ]
12457                     }
12458                 ]
12459
12460             })
12461         }
12462         
12463         if(this.multiple){
12464             combobox.cls += ' roo-select2-container-multi';
12465         }
12466          var indicator = {
12467             tag : 'i',
12468             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12469             tooltip : 'This field is required'
12470         };
12471         if (Roo.bootstrap.version == 4) {
12472             indicator = {
12473                 tag : 'i',
12474                 style : 'display:none'
12475             };
12476         }
12477         
12478         
12479         if (align ==='left' && this.fieldLabel.length) {
12480             
12481             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12482
12483             cfg.cn = [
12484                 indicator,
12485                 {
12486                     tag: 'label',
12487                     'for' :  id,
12488                     cls : 'control-label',
12489                     html : this.fieldLabel
12490
12491                 },
12492                 {
12493                     cls : "", 
12494                     cn: [
12495                         combobox
12496                     ]
12497                 }
12498
12499             ];
12500             
12501             var labelCfg = cfg.cn[1];
12502             var contentCfg = cfg.cn[2];
12503             
12504             if(this.indicatorpos == 'right'){
12505                 cfg.cn = [
12506                     {
12507                         tag: 'label',
12508                         'for' :  id,
12509                         cls : 'control-label',
12510                         cn : [
12511                             {
12512                                 tag : 'span',
12513                                 html : this.fieldLabel
12514                             },
12515                             indicator
12516                         ]
12517                     },
12518                     {
12519                         cls : "", 
12520                         cn: [
12521                             combobox
12522                         ]
12523                     }
12524
12525                 ];
12526                 
12527                 labelCfg = cfg.cn[0];
12528                 contentCfg = cfg.cn[1];
12529             }
12530             
12531             if(this.labelWidth > 12){
12532                 labelCfg.style = "width: " + this.labelWidth + 'px';
12533             }
12534             
12535             if(this.labelWidth < 13 && this.labelmd == 0){
12536                 this.labelmd = this.labelWidth;
12537             }
12538             
12539             if(this.labellg > 0){
12540                 labelCfg.cls += ' col-lg-' + this.labellg;
12541                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12542             }
12543             
12544             if(this.labelmd > 0){
12545                 labelCfg.cls += ' col-md-' + this.labelmd;
12546                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12547             }
12548             
12549             if(this.labelsm > 0){
12550                 labelCfg.cls += ' col-sm-' + this.labelsm;
12551                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12552             }
12553             
12554             if(this.labelxs > 0){
12555                 labelCfg.cls += ' col-xs-' + this.labelxs;
12556                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12557             }
12558             
12559         } else if ( this.fieldLabel.length) {
12560 //                Roo.log(" label");
12561             cfg.cn = [
12562                 indicator,
12563                {
12564                    tag: 'label',
12565                    //cls : 'input-group-addon',
12566                    html : this.fieldLabel
12567
12568                },
12569
12570                combobox
12571
12572             ];
12573             
12574             if(this.indicatorpos == 'right'){
12575                 
12576                 cfg.cn = [
12577                     {
12578                        tag: 'label',
12579                        cn : [
12580                            {
12581                                tag : 'span',
12582                                html : this.fieldLabel
12583                            },
12584                            indicator
12585                        ]
12586
12587                     },
12588                     combobox
12589
12590                 ];
12591
12592             }
12593
12594         } else {
12595             
12596 //                Roo.log(" no label && no align");
12597                 cfg = combobox
12598                      
12599                 
12600         }
12601         
12602         var settings=this;
12603         ['xs','sm','md','lg'].map(function(size){
12604             if (settings[size]) {
12605                 cfg.cls += ' col-' + size + '-' + settings[size];
12606             }
12607         });
12608         
12609         return cfg;
12610         
12611     },
12612     
12613     
12614     
12615     // private
12616     onResize : function(w, h){
12617 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12618 //        if(typeof w == 'number'){
12619 //            var x = w - this.trigger.getWidth();
12620 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12621 //            this.trigger.setStyle('left', x+'px');
12622 //        }
12623     },
12624
12625     // private
12626     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12627
12628     // private
12629     getResizeEl : function(){
12630         return this.inputEl();
12631     },
12632
12633     // private
12634     getPositionEl : function(){
12635         return this.inputEl();
12636     },
12637
12638     // private
12639     alignErrorIcon : function(){
12640         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12641     },
12642
12643     // private
12644     initEvents : function(){
12645         
12646         this.createList();
12647         
12648         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12649         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12650         if(!this.multiple && this.showToggleBtn){
12651             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12652             if(this.hideTrigger){
12653                 this.trigger.setDisplayed(false);
12654             }
12655             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12656         }
12657         
12658         if(this.multiple){
12659             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12660         }
12661         
12662         if(this.removable && !this.editable && !this.tickable){
12663             var close = this.closeTriggerEl();
12664             
12665             if(close){
12666                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12667                 close.on('click', this.removeBtnClick, this, close);
12668             }
12669         }
12670         
12671         //this.trigger.addClassOnOver('x-form-trigger-over');
12672         //this.trigger.addClassOnClick('x-form-trigger-click');
12673         
12674         //if(!this.width){
12675         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12676         //}
12677     },
12678     
12679     closeTriggerEl : function()
12680     {
12681         var close = this.el.select('.roo-combo-removable-btn', true).first();
12682         return close ? close : false;
12683     },
12684     
12685     removeBtnClick : function(e, h, el)
12686     {
12687         e.preventDefault();
12688         
12689         if(this.fireEvent("remove", this) !== false){
12690             this.reset();
12691             this.fireEvent("afterremove", this)
12692         }
12693     },
12694     
12695     createList : function()
12696     {
12697         this.list = Roo.get(document.body).createChild({
12698             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12699             cls: 'typeahead typeahead-long dropdown-menu shadow',
12700             style: 'display:none'
12701         });
12702         
12703         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12704         
12705     },
12706
12707     // private
12708     initTrigger : function(){
12709        
12710     },
12711
12712     // private
12713     onDestroy : function(){
12714         if(this.trigger){
12715             this.trigger.removeAllListeners();
12716           //  this.trigger.remove();
12717         }
12718         //if(this.wrap){
12719         //    this.wrap.remove();
12720         //}
12721         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12722     },
12723
12724     // private
12725     onFocus : function(){
12726         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12727         /*
12728         if(!this.mimicing){
12729             this.wrap.addClass('x-trigger-wrap-focus');
12730             this.mimicing = true;
12731             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12732             if(this.monitorTab){
12733                 this.el.on("keydown", this.checkTab, this);
12734             }
12735         }
12736         */
12737     },
12738
12739     // private
12740     checkTab : function(e){
12741         if(e.getKey() == e.TAB){
12742             this.triggerBlur();
12743         }
12744     },
12745
12746     // private
12747     onBlur : function(){
12748         // do nothing
12749     },
12750
12751     // private
12752     mimicBlur : function(e, t){
12753         /*
12754         if(!this.wrap.contains(t) && this.validateBlur()){
12755             this.triggerBlur();
12756         }
12757         */
12758     },
12759
12760     // private
12761     triggerBlur : function(){
12762         this.mimicing = false;
12763         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12764         if(this.monitorTab){
12765             this.el.un("keydown", this.checkTab, this);
12766         }
12767         //this.wrap.removeClass('x-trigger-wrap-focus');
12768         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12769     },
12770
12771     // private
12772     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12773     validateBlur : function(e, t){
12774         return true;
12775     },
12776
12777     // private
12778     onDisable : function(){
12779         this.inputEl().dom.disabled = true;
12780         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12781         //if(this.wrap){
12782         //    this.wrap.addClass('x-item-disabled');
12783         //}
12784     },
12785
12786     // private
12787     onEnable : function(){
12788         this.inputEl().dom.disabled = false;
12789         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12790         //if(this.wrap){
12791         //    this.el.removeClass('x-item-disabled');
12792         //}
12793     },
12794
12795     // private
12796     onShow : function(){
12797         var ae = this.getActionEl();
12798         
12799         if(ae){
12800             ae.dom.style.display = '';
12801             ae.dom.style.visibility = 'visible';
12802         }
12803     },
12804
12805     // private
12806     
12807     onHide : function(){
12808         var ae = this.getActionEl();
12809         ae.dom.style.display = 'none';
12810     },
12811
12812     /**
12813      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12814      * by an implementing function.
12815      * @method
12816      * @param {EventObject} e
12817      */
12818     onTriggerClick : Roo.emptyFn
12819 });
12820  
12821 /*
12822 * Licence: LGPL
12823 */
12824
12825 /**
12826  * @class Roo.bootstrap.CardUploader
12827  * @extends Roo.bootstrap.Button
12828  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12829  * @cfg {Number} errorTimeout default 3000
12830  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12831  * @cfg {Array}  html The button text.
12832
12833  *
12834  * @constructor
12835  * Create a new CardUploader
12836  * @param {Object} config The config object
12837  */
12838
12839 Roo.bootstrap.CardUploader = function(config){
12840     
12841  
12842     
12843     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12844     
12845     
12846     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12847         return r.data.id
12848      });
12849     
12850      this.addEvents({
12851          // raw events
12852         /**
12853          * @event preview
12854          * When a image is clicked on - and needs to display a slideshow or similar..
12855          * @param {Roo.bootstrap.Card} this
12856          * @param {Object} The image information data 
12857          *
12858          */
12859         'preview' : true,
12860          /**
12861          * @event download
12862          * When a the download link is clicked
12863          * @param {Roo.bootstrap.Card} this
12864          * @param {Object} The image information data  contains 
12865          */
12866         'download' : true
12867         
12868     });
12869 };
12870  
12871 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12872     
12873      
12874     errorTimeout : 3000,
12875      
12876     images : false,
12877    
12878     fileCollection : false,
12879     allowBlank : true,
12880     
12881     getAutoCreate : function()
12882     {
12883         
12884         var cfg =  {
12885             cls :'form-group' ,
12886             cn : [
12887                
12888                 {
12889                     tag: 'label',
12890                    //cls : 'input-group-addon',
12891                     html : this.fieldLabel
12892
12893                 },
12894
12895                 {
12896                     tag: 'input',
12897                     type : 'hidden',
12898                     name : this.name,
12899                     value : this.value,
12900                     cls : 'd-none  form-control'
12901                 },
12902                 
12903                 {
12904                     tag: 'input',
12905                     multiple : 'multiple',
12906                     type : 'file',
12907                     cls : 'd-none  roo-card-upload-selector'
12908                 },
12909                 
12910                 {
12911                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12912                 },
12913                 {
12914                     cls : 'card-columns roo-card-uploader-container'
12915                 }
12916
12917             ]
12918         };
12919            
12920          
12921         return cfg;
12922     },
12923     
12924     getChildContainer : function() /// what children are added to.
12925     {
12926         return this.containerEl;
12927     },
12928    
12929     getButtonContainer : function() /// what children are added to.
12930     {
12931         return this.el.select(".roo-card-uploader-button-container").first();
12932     },
12933    
12934     initEvents : function()
12935     {
12936         
12937         Roo.bootstrap.Input.prototype.initEvents.call(this);
12938         
12939         var t = this;
12940         this.addxtype({
12941             xns: Roo.bootstrap,
12942
12943             xtype : 'Button',
12944             container_method : 'getButtonContainer' ,            
12945             html :  this.html, // fix changable?
12946             cls : 'w-100 ',
12947             listeners : {
12948                 'click' : function(btn, e) {
12949                     t.onClick(e);
12950                 }
12951             }
12952         });
12953         
12954         
12955         
12956         
12957         this.urlAPI = (window.createObjectURL && window) || 
12958                                 (window.URL && URL.revokeObjectURL && URL) || 
12959                                 (window.webkitURL && webkitURL);
12960                         
12961          
12962          
12963          
12964         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12965         
12966         this.selectorEl.on('change', this.onFileSelected, this);
12967         if (this.images) {
12968             var t = this;
12969             this.images.forEach(function(img) {
12970                 t.addCard(img)
12971             });
12972             this.images = false;
12973         }
12974         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12975          
12976        
12977     },
12978     
12979    
12980     onClick : function(e)
12981     {
12982         e.preventDefault();
12983          
12984         this.selectorEl.dom.click();
12985          
12986     },
12987     
12988     onFileSelected : function(e)
12989     {
12990         e.preventDefault();
12991         
12992         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12993             return;
12994         }
12995         
12996         Roo.each(this.selectorEl.dom.files, function(file){    
12997             this.addFile(file);
12998         }, this);
12999          
13000     },
13001     
13002       
13003     
13004       
13005     
13006     addFile : function(file)
13007     {
13008            
13009         if(typeof(file) === 'string'){
13010             throw "Add file by name?"; // should not happen
13011             return;
13012         }
13013         
13014         if(!file || !this.urlAPI){
13015             return;
13016         }
13017         
13018         // file;
13019         // file.type;
13020         
13021         var _this = this;
13022         
13023         
13024         var url = _this.urlAPI.createObjectURL( file);
13025            
13026         this.addCard({
13027             id : Roo.bootstrap.CardUploader.ID--,
13028             is_uploaded : false,
13029             src : url,
13030             srcfile : file,
13031             title : file.name,
13032             mimetype : file.type,
13033             preview : false,
13034             is_deleted : 0
13035         });
13036         
13037     },
13038     
13039     /**
13040      * addCard - add an Attachment to the uploader
13041      * @param data - the data about the image to upload
13042      *
13043      * {
13044           id : 123
13045           title : "Title of file",
13046           is_uploaded : false,
13047           src : "http://.....",
13048           srcfile : { the File upload object },
13049           mimetype : file.type,
13050           preview : false,
13051           is_deleted : 0
13052           .. any other data...
13053         }
13054      *
13055      * 
13056     */
13057     
13058     addCard : function (data)
13059     {
13060         // hidden input element?
13061         // if the file is not an image...
13062         //then we need to use something other that and header_image
13063         var t = this;
13064         //   remove.....
13065         var footer = [
13066             {
13067                 xns : Roo.bootstrap,
13068                 xtype : 'CardFooter',
13069                  items: [
13070                     {
13071                         xns : Roo.bootstrap,
13072                         xtype : 'Element',
13073                         cls : 'd-flex',
13074                         items : [
13075                             
13076                             {
13077                                 xns : Roo.bootstrap,
13078                                 xtype : 'Button',
13079                                 html : String.format("<small>{0}</small>", data.title),
13080                                 cls : 'col-10 text-left',
13081                                 size: 'sm',
13082                                 weight: 'link',
13083                                 fa : 'download',
13084                                 listeners : {
13085                                     click : function() {
13086                                      
13087                                         t.fireEvent( "download", t, data );
13088                                     }
13089                                 }
13090                             },
13091                           
13092                             {
13093                                 xns : Roo.bootstrap,
13094                                 xtype : 'Button',
13095                                 style: 'max-height: 28px; ',
13096                                 size : 'sm',
13097                                 weight: 'danger',
13098                                 cls : 'col-2',
13099                                 fa : 'times',
13100                                 listeners : {
13101                                     click : function() {
13102                                         t.removeCard(data.id)
13103                                     }
13104                                 }
13105                             }
13106                         ]
13107                     }
13108                     
13109                 ] 
13110             }
13111             
13112         ];
13113         
13114         var cn = this.addxtype(
13115             {
13116                  
13117                 xns : Roo.bootstrap,
13118                 xtype : 'Card',
13119                 closeable : true,
13120                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13121                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13122                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13123                 data : data,
13124                 html : false,
13125                  
13126                 items : footer,
13127                 initEvents : function() {
13128                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13129                     var card = this;
13130                     this.imgEl = this.el.select('.card-img-top').first();
13131                     if (this.imgEl) {
13132                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13133                         this.imgEl.set({ 'pointer' : 'cursor' });
13134                                   
13135                     }
13136                     this.getCardFooter().addClass('p-1');
13137                     
13138                   
13139                 }
13140                 
13141             }
13142         );
13143         // dont' really need ot update items.
13144         // this.items.push(cn);
13145         this.fileCollection.add(cn);
13146         
13147         if (!data.srcfile) {
13148             this.updateInput();
13149             return;
13150         }
13151             
13152         var _t = this;
13153         var reader = new FileReader();
13154         reader.addEventListener("load", function() {  
13155             data.srcdata =  reader.result;
13156             _t.updateInput();
13157         });
13158         reader.readAsDataURL(data.srcfile);
13159         
13160         
13161         
13162     },
13163     removeCard : function(id)
13164     {
13165         
13166         var card  = this.fileCollection.get(id);
13167         card.data.is_deleted = 1;
13168         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13169         //this.fileCollection.remove(card);
13170         //this.items = this.items.filter(function(e) { return e != card });
13171         // dont' really need ot update items.
13172         card.el.dom.parentNode.removeChild(card.el.dom);
13173         this.updateInput();
13174
13175         
13176     },
13177     reset: function()
13178     {
13179         this.fileCollection.each(function(card) {
13180             if (card.el.dom && card.el.dom.parentNode) {
13181                 card.el.dom.parentNode.removeChild(card.el.dom);
13182             }
13183         });
13184         this.fileCollection.clear();
13185         this.updateInput();
13186     },
13187     
13188     updateInput : function()
13189     {
13190          var data = [];
13191         this.fileCollection.each(function(e) {
13192             data.push(e.data);
13193             
13194         });
13195         this.inputEl().dom.value = JSON.stringify(data);
13196         
13197         
13198         
13199     }
13200     
13201     
13202 });
13203
13204
13205 Roo.bootstrap.CardUploader.ID = -1;/*
13206  * Based on:
13207  * Ext JS Library 1.1.1
13208  * Copyright(c) 2006-2007, Ext JS, LLC.
13209  *
13210  * Originally Released Under LGPL - original licence link has changed is not relivant.
13211  *
13212  * Fork - LGPL
13213  * <script type="text/javascript">
13214  */
13215
13216
13217 /**
13218  * @class Roo.data.SortTypes
13219  * @singleton
13220  * Defines the default sorting (casting?) comparison functions used when sorting data.
13221  */
13222 Roo.data.SortTypes = {
13223     /**
13224      * Default sort that does nothing
13225      * @param {Mixed} s The value being converted
13226      * @return {Mixed} The comparison value
13227      */
13228     none : function(s){
13229         return s;
13230     },
13231     
13232     /**
13233      * The regular expression used to strip tags
13234      * @type {RegExp}
13235      * @property
13236      */
13237     stripTagsRE : /<\/?[^>]+>/gi,
13238     
13239     /**
13240      * Strips all HTML tags to sort on text only
13241      * @param {Mixed} s The value being converted
13242      * @return {String} The comparison value
13243      */
13244     asText : function(s){
13245         return String(s).replace(this.stripTagsRE, "");
13246     },
13247     
13248     /**
13249      * Strips all HTML tags to sort on text only - Case insensitive
13250      * @param {Mixed} s The value being converted
13251      * @return {String} The comparison value
13252      */
13253     asUCText : function(s){
13254         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13255     },
13256     
13257     /**
13258      * Case insensitive string
13259      * @param {Mixed} s The value being converted
13260      * @return {String} The comparison value
13261      */
13262     asUCString : function(s) {
13263         return String(s).toUpperCase();
13264     },
13265     
13266     /**
13267      * Date sorting
13268      * @param {Mixed} s The value being converted
13269      * @return {Number} The comparison value
13270      */
13271     asDate : function(s) {
13272         if(!s){
13273             return 0;
13274         }
13275         if(s instanceof Date){
13276             return s.getTime();
13277         }
13278         return Date.parse(String(s));
13279     },
13280     
13281     /**
13282      * Float sorting
13283      * @param {Mixed} s The value being converted
13284      * @return {Float} The comparison value
13285      */
13286     asFloat : function(s) {
13287         var val = parseFloat(String(s).replace(/,/g, ""));
13288         if(isNaN(val)) {
13289             val = 0;
13290         }
13291         return val;
13292     },
13293     
13294     /**
13295      * Integer sorting
13296      * @param {Mixed} s The value being converted
13297      * @return {Number} The comparison value
13298      */
13299     asInt : function(s) {
13300         var val = parseInt(String(s).replace(/,/g, ""));
13301         if(isNaN(val)) {
13302             val = 0;
13303         }
13304         return val;
13305     }
13306 };/*
13307  * Based on:
13308  * Ext JS Library 1.1.1
13309  * Copyright(c) 2006-2007, Ext JS, LLC.
13310  *
13311  * Originally Released Under LGPL - original licence link has changed is not relivant.
13312  *
13313  * Fork - LGPL
13314  * <script type="text/javascript">
13315  */
13316
13317 /**
13318 * @class Roo.data.Record
13319  * Instances of this class encapsulate both record <em>definition</em> information, and record
13320  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13321  * to access Records cached in an {@link Roo.data.Store} object.<br>
13322  * <p>
13323  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13324  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13325  * objects.<br>
13326  * <p>
13327  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13328  * @constructor
13329  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13330  * {@link #create}. The parameters are the same.
13331  * @param {Array} data An associative Array of data values keyed by the field name.
13332  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13333  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13334  * not specified an integer id is generated.
13335  */
13336 Roo.data.Record = function(data, id){
13337     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13338     this.data = data;
13339 };
13340
13341 /**
13342  * Generate a constructor for a specific record layout.
13343  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13344  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13345  * Each field definition object may contain the following properties: <ul>
13346  * <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,
13347  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13348  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13349  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13350  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13351  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13352  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13353  * this may be omitted.</p></li>
13354  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13355  * <ul><li>auto (Default, implies no conversion)</li>
13356  * <li>string</li>
13357  * <li>int</li>
13358  * <li>float</li>
13359  * <li>boolean</li>
13360  * <li>date</li></ul></p></li>
13361  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13362  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13363  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13364  * by the Reader into an object that will be stored in the Record. It is passed the
13365  * following parameters:<ul>
13366  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13367  * </ul></p></li>
13368  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13369  * </ul>
13370  * <br>usage:<br><pre><code>
13371 var TopicRecord = Roo.data.Record.create(
13372     {name: 'title', mapping: 'topic_title'},
13373     {name: 'author', mapping: 'username'},
13374     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13375     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13376     {name: 'lastPoster', mapping: 'user2'},
13377     {name: 'excerpt', mapping: 'post_text'}
13378 );
13379
13380 var myNewRecord = new TopicRecord({
13381     title: 'Do my job please',
13382     author: 'noobie',
13383     totalPosts: 1,
13384     lastPost: new Date(),
13385     lastPoster: 'Animal',
13386     excerpt: 'No way dude!'
13387 });
13388 myStore.add(myNewRecord);
13389 </code></pre>
13390  * @method create
13391  * @static
13392  */
13393 Roo.data.Record.create = function(o){
13394     var f = function(){
13395         f.superclass.constructor.apply(this, arguments);
13396     };
13397     Roo.extend(f, Roo.data.Record);
13398     var p = f.prototype;
13399     p.fields = new Roo.util.MixedCollection(false, function(field){
13400         return field.name;
13401     });
13402     for(var i = 0, len = o.length; i < len; i++){
13403         p.fields.add(new Roo.data.Field(o[i]));
13404     }
13405     f.getField = function(name){
13406         return p.fields.get(name);  
13407     };
13408     return f;
13409 };
13410
13411 Roo.data.Record.AUTO_ID = 1000;
13412 Roo.data.Record.EDIT = 'edit';
13413 Roo.data.Record.REJECT = 'reject';
13414 Roo.data.Record.COMMIT = 'commit';
13415
13416 Roo.data.Record.prototype = {
13417     /**
13418      * Readonly flag - true if this record has been modified.
13419      * @type Boolean
13420      */
13421     dirty : false,
13422     editing : false,
13423     error: null,
13424     modified: null,
13425
13426     // private
13427     join : function(store){
13428         this.store = store;
13429     },
13430
13431     /**
13432      * Set the named field to the specified value.
13433      * @param {String} name The name of the field to set.
13434      * @param {Object} value The value to set the field to.
13435      */
13436     set : function(name, value){
13437         if(this.data[name] == value){
13438             return;
13439         }
13440         this.dirty = true;
13441         if(!this.modified){
13442             this.modified = {};
13443         }
13444         if(typeof this.modified[name] == 'undefined'){
13445             this.modified[name] = this.data[name];
13446         }
13447         this.data[name] = value;
13448         if(!this.editing && this.store){
13449             this.store.afterEdit(this);
13450         }       
13451     },
13452
13453     /**
13454      * Get the value of the named field.
13455      * @param {String} name The name of the field to get the value of.
13456      * @return {Object} The value of the field.
13457      */
13458     get : function(name){
13459         return this.data[name]; 
13460     },
13461
13462     // private
13463     beginEdit : function(){
13464         this.editing = true;
13465         this.modified = {}; 
13466     },
13467
13468     // private
13469     cancelEdit : function(){
13470         this.editing = false;
13471         delete this.modified;
13472     },
13473
13474     // private
13475     endEdit : function(){
13476         this.editing = false;
13477         if(this.dirty && this.store){
13478             this.store.afterEdit(this);
13479         }
13480     },
13481
13482     /**
13483      * Usually called by the {@link Roo.data.Store} which owns the Record.
13484      * Rejects all changes made to the Record since either creation, or the last commit operation.
13485      * Modified fields are reverted to their original values.
13486      * <p>
13487      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13488      * of reject operations.
13489      */
13490     reject : function(){
13491         var m = this.modified;
13492         for(var n in m){
13493             if(typeof m[n] != "function"){
13494                 this.data[n] = m[n];
13495             }
13496         }
13497         this.dirty = false;
13498         delete this.modified;
13499         this.editing = false;
13500         if(this.store){
13501             this.store.afterReject(this);
13502         }
13503     },
13504
13505     /**
13506      * Usually called by the {@link Roo.data.Store} which owns the Record.
13507      * Commits all changes made to the Record since either creation, or the last commit operation.
13508      * <p>
13509      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13510      * of commit operations.
13511      */
13512     commit : function(){
13513         this.dirty = false;
13514         delete this.modified;
13515         this.editing = false;
13516         if(this.store){
13517             this.store.afterCommit(this);
13518         }
13519     },
13520
13521     // private
13522     hasError : function(){
13523         return this.error != null;
13524     },
13525
13526     // private
13527     clearError : function(){
13528         this.error = null;
13529     },
13530
13531     /**
13532      * Creates a copy of this record.
13533      * @param {String} id (optional) A new record id if you don't want to use this record's id
13534      * @return {Record}
13535      */
13536     copy : function(newId) {
13537         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13538     }
13539 };/*
13540  * Based on:
13541  * Ext JS Library 1.1.1
13542  * Copyright(c) 2006-2007, Ext JS, LLC.
13543  *
13544  * Originally Released Under LGPL - original licence link has changed is not relivant.
13545  *
13546  * Fork - LGPL
13547  * <script type="text/javascript">
13548  */
13549
13550
13551
13552 /**
13553  * @class Roo.data.Store
13554  * @extends Roo.util.Observable
13555  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13556  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13557  * <p>
13558  * 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
13559  * has no knowledge of the format of the data returned by the Proxy.<br>
13560  * <p>
13561  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13562  * instances from the data object. These records are cached and made available through accessor functions.
13563  * @constructor
13564  * Creates a new Store.
13565  * @param {Object} config A config object containing the objects needed for the Store to access data,
13566  * and read the data into Records.
13567  */
13568 Roo.data.Store = function(config){
13569     this.data = new Roo.util.MixedCollection(false);
13570     this.data.getKey = function(o){
13571         return o.id;
13572     };
13573     this.baseParams = {};
13574     // private
13575     this.paramNames = {
13576         "start" : "start",
13577         "limit" : "limit",
13578         "sort" : "sort",
13579         "dir" : "dir",
13580         "multisort" : "_multisort"
13581     };
13582
13583     if(config && config.data){
13584         this.inlineData = config.data;
13585         delete config.data;
13586     }
13587
13588     Roo.apply(this, config);
13589     
13590     if(this.reader){ // reader passed
13591         this.reader = Roo.factory(this.reader, Roo.data);
13592         this.reader.xmodule = this.xmodule || false;
13593         if(!this.recordType){
13594             this.recordType = this.reader.recordType;
13595         }
13596         if(this.reader.onMetaChange){
13597             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13598         }
13599     }
13600
13601     if(this.recordType){
13602         this.fields = this.recordType.prototype.fields;
13603     }
13604     this.modified = [];
13605
13606     this.addEvents({
13607         /**
13608          * @event datachanged
13609          * Fires when the data cache has changed, and a widget which is using this Store
13610          * as a Record cache should refresh its view.
13611          * @param {Store} this
13612          */
13613         datachanged : true,
13614         /**
13615          * @event metachange
13616          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13617          * @param {Store} this
13618          * @param {Object} meta The JSON metadata
13619          */
13620         metachange : true,
13621         /**
13622          * @event add
13623          * Fires when Records have been added to the Store
13624          * @param {Store} this
13625          * @param {Roo.data.Record[]} records The array of Records added
13626          * @param {Number} index The index at which the record(s) were added
13627          */
13628         add : true,
13629         /**
13630          * @event remove
13631          * Fires when a Record has been removed from the Store
13632          * @param {Store} this
13633          * @param {Roo.data.Record} record The Record that was removed
13634          * @param {Number} index The index at which the record was removed
13635          */
13636         remove : true,
13637         /**
13638          * @event update
13639          * Fires when a Record has been updated
13640          * @param {Store} this
13641          * @param {Roo.data.Record} record The Record that was updated
13642          * @param {String} operation The update operation being performed.  Value may be one of:
13643          * <pre><code>
13644  Roo.data.Record.EDIT
13645  Roo.data.Record.REJECT
13646  Roo.data.Record.COMMIT
13647          * </code></pre>
13648          */
13649         update : true,
13650         /**
13651          * @event clear
13652          * Fires when the data cache has been cleared.
13653          * @param {Store} this
13654          */
13655         clear : true,
13656         /**
13657          * @event beforeload
13658          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13659          * the load action will be canceled.
13660          * @param {Store} this
13661          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13662          */
13663         beforeload : true,
13664         /**
13665          * @event beforeloadadd
13666          * Fires after a new set of Records has been loaded.
13667          * @param {Store} this
13668          * @param {Roo.data.Record[]} records The Records that were loaded
13669          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13670          */
13671         beforeloadadd : true,
13672         /**
13673          * @event load
13674          * Fires after a new set of Records has been loaded, before they are added to the store.
13675          * @param {Store} this
13676          * @param {Roo.data.Record[]} records The Records that were loaded
13677          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13678          * @params {Object} return from reader
13679          */
13680         load : true,
13681         /**
13682          * @event loadexception
13683          * Fires if an exception occurs in the Proxy during loading.
13684          * Called with the signature of the Proxy's "loadexception" event.
13685          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13686          * 
13687          * @param {Proxy} 
13688          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13689          * @param {Object} load options 
13690          * @param {Object} jsonData from your request (normally this contains the Exception)
13691          */
13692         loadexception : true
13693     });
13694     
13695     if(this.proxy){
13696         this.proxy = Roo.factory(this.proxy, Roo.data);
13697         this.proxy.xmodule = this.xmodule || false;
13698         this.relayEvents(this.proxy,  ["loadexception"]);
13699     }
13700     this.sortToggle = {};
13701     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13702
13703     Roo.data.Store.superclass.constructor.call(this);
13704
13705     if(this.inlineData){
13706         this.loadData(this.inlineData);
13707         delete this.inlineData;
13708     }
13709 };
13710
13711 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13712      /**
13713     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13714     * without a remote query - used by combo/forms at present.
13715     */
13716     
13717     /**
13718     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13719     */
13720     /**
13721     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13722     */
13723     /**
13724     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13725     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13726     */
13727     /**
13728     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13729     * on any HTTP request
13730     */
13731     /**
13732     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13733     */
13734     /**
13735     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13736     */
13737     multiSort: false,
13738     /**
13739     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13740     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13741     */
13742     remoteSort : false,
13743
13744     /**
13745     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13746      * loaded or when a record is removed. (defaults to false).
13747     */
13748     pruneModifiedRecords : false,
13749
13750     // private
13751     lastOptions : null,
13752
13753     /**
13754      * Add Records to the Store and fires the add event.
13755      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13756      */
13757     add : function(records){
13758         records = [].concat(records);
13759         for(var i = 0, len = records.length; i < len; i++){
13760             records[i].join(this);
13761         }
13762         var index = this.data.length;
13763         this.data.addAll(records);
13764         this.fireEvent("add", this, records, index);
13765     },
13766
13767     /**
13768      * Remove a Record from the Store and fires the remove event.
13769      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13770      */
13771     remove : function(record){
13772         var index = this.data.indexOf(record);
13773         this.data.removeAt(index);
13774  
13775         if(this.pruneModifiedRecords){
13776             this.modified.remove(record);
13777         }
13778         this.fireEvent("remove", this, record, index);
13779     },
13780
13781     /**
13782      * Remove all Records from the Store and fires the clear event.
13783      */
13784     removeAll : function(){
13785         this.data.clear();
13786         if(this.pruneModifiedRecords){
13787             this.modified = [];
13788         }
13789         this.fireEvent("clear", this);
13790     },
13791
13792     /**
13793      * Inserts Records to the Store at the given index and fires the add event.
13794      * @param {Number} index The start index at which to insert the passed Records.
13795      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13796      */
13797     insert : function(index, records){
13798         records = [].concat(records);
13799         for(var i = 0, len = records.length; i < len; i++){
13800             this.data.insert(index, records[i]);
13801             records[i].join(this);
13802         }
13803         this.fireEvent("add", this, records, index);
13804     },
13805
13806     /**
13807      * Get the index within the cache of the passed Record.
13808      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13809      * @return {Number} The index of the passed Record. Returns -1 if not found.
13810      */
13811     indexOf : function(record){
13812         return this.data.indexOf(record);
13813     },
13814
13815     /**
13816      * Get the index within the cache of the Record with the passed id.
13817      * @param {String} id The id of the Record to find.
13818      * @return {Number} The index of the Record. Returns -1 if not found.
13819      */
13820     indexOfId : function(id){
13821         return this.data.indexOfKey(id);
13822     },
13823
13824     /**
13825      * Get the Record with the specified id.
13826      * @param {String} id The id of the Record to find.
13827      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13828      */
13829     getById : function(id){
13830         return this.data.key(id);
13831     },
13832
13833     /**
13834      * Get the Record at the specified index.
13835      * @param {Number} index The index of the Record to find.
13836      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13837      */
13838     getAt : function(index){
13839         return this.data.itemAt(index);
13840     },
13841
13842     /**
13843      * Returns a range of Records between specified indices.
13844      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13845      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13846      * @return {Roo.data.Record[]} An array of Records
13847      */
13848     getRange : function(start, end){
13849         return this.data.getRange(start, end);
13850     },
13851
13852     // private
13853     storeOptions : function(o){
13854         o = Roo.apply({}, o);
13855         delete o.callback;
13856         delete o.scope;
13857         this.lastOptions = o;
13858     },
13859
13860     /**
13861      * Loads the Record cache from the configured Proxy using the configured Reader.
13862      * <p>
13863      * If using remote paging, then the first load call must specify the <em>start</em>
13864      * and <em>limit</em> properties in the options.params property to establish the initial
13865      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13866      * <p>
13867      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13868      * and this call will return before the new data has been loaded. Perform any post-processing
13869      * in a callback function, or in a "load" event handler.</strong>
13870      * <p>
13871      * @param {Object} options An object containing properties which control loading options:<ul>
13872      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13873      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13874      * passed the following arguments:<ul>
13875      * <li>r : Roo.data.Record[]</li>
13876      * <li>options: Options object from the load call</li>
13877      * <li>success: Boolean success indicator</li></ul></li>
13878      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13879      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13880      * </ul>
13881      */
13882     load : function(options){
13883         options = options || {};
13884         if(this.fireEvent("beforeload", this, options) !== false){
13885             this.storeOptions(options);
13886             var p = Roo.apply(options.params || {}, this.baseParams);
13887             // if meta was not loaded from remote source.. try requesting it.
13888             if (!this.reader.metaFromRemote) {
13889                 p._requestMeta = 1;
13890             }
13891             if(this.sortInfo && this.remoteSort){
13892                 var pn = this.paramNames;
13893                 p[pn["sort"]] = this.sortInfo.field;
13894                 p[pn["dir"]] = this.sortInfo.direction;
13895             }
13896             if (this.multiSort) {
13897                 var pn = this.paramNames;
13898                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13899             }
13900             
13901             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13902         }
13903     },
13904
13905     /**
13906      * Reloads the Record cache from the configured Proxy using the configured Reader and
13907      * the options from the last load operation performed.
13908      * @param {Object} options (optional) An object containing properties which may override the options
13909      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13910      * the most recently used options are reused).
13911      */
13912     reload : function(options){
13913         this.load(Roo.applyIf(options||{}, this.lastOptions));
13914     },
13915
13916     // private
13917     // Called as a callback by the Reader during a load operation.
13918     loadRecords : function(o, options, success){
13919         if(!o || success === false){
13920             if(success !== false){
13921                 this.fireEvent("load", this, [], options, o);
13922             }
13923             if(options.callback){
13924                 options.callback.call(options.scope || this, [], options, false);
13925             }
13926             return;
13927         }
13928         // if data returned failure - throw an exception.
13929         if (o.success === false) {
13930             // show a message if no listener is registered.
13931             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13932                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13933             }
13934             // loadmask wil be hooked into this..
13935             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13936             return;
13937         }
13938         var r = o.records, t = o.totalRecords || r.length;
13939         
13940         this.fireEvent("beforeloadadd", this, r, options, o);
13941         
13942         if(!options || options.add !== true){
13943             if(this.pruneModifiedRecords){
13944                 this.modified = [];
13945             }
13946             for(var i = 0, len = r.length; i < len; i++){
13947                 r[i].join(this);
13948             }
13949             if(this.snapshot){
13950                 this.data = this.snapshot;
13951                 delete this.snapshot;
13952             }
13953             this.data.clear();
13954             this.data.addAll(r);
13955             this.totalLength = t;
13956             this.applySort();
13957             this.fireEvent("datachanged", this);
13958         }else{
13959             this.totalLength = Math.max(t, this.data.length+r.length);
13960             this.add(r);
13961         }
13962         
13963         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13964                 
13965             var e = new Roo.data.Record({});
13966
13967             e.set(this.parent.displayField, this.parent.emptyTitle);
13968             e.set(this.parent.valueField, '');
13969
13970             this.insert(0, e);
13971         }
13972             
13973         this.fireEvent("load", this, r, options, o);
13974         if(options.callback){
13975             options.callback.call(options.scope || this, r, options, true);
13976         }
13977     },
13978
13979
13980     /**
13981      * Loads data from a passed data block. A Reader which understands the format of the data
13982      * must have been configured in the constructor.
13983      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13984      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13985      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13986      */
13987     loadData : function(o, append){
13988         var r = this.reader.readRecords(o);
13989         this.loadRecords(r, {add: append}, true);
13990     },
13991     
13992      /**
13993      * using 'cn' the nested child reader read the child array into it's child stores.
13994      * @param {Object} rec The record with a 'children array
13995      */
13996     loadDataFromChildren : function(rec)
13997     {
13998         this.loadData(this.reader.toLoadData(rec));
13999     },
14000     
14001
14002     /**
14003      * Gets the number of cached records.
14004      * <p>
14005      * <em>If using paging, this may not be the total size of the dataset. If the data object
14006      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14007      * the data set size</em>
14008      */
14009     getCount : function(){
14010         return this.data.length || 0;
14011     },
14012
14013     /**
14014      * Gets the total number of records in the dataset as returned by the server.
14015      * <p>
14016      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14017      * the dataset size</em>
14018      */
14019     getTotalCount : function(){
14020         return this.totalLength || 0;
14021     },
14022
14023     /**
14024      * Returns the sort state of the Store as an object with two properties:
14025      * <pre><code>
14026  field {String} The name of the field by which the Records are sorted
14027  direction {String} The sort order, "ASC" or "DESC"
14028      * </code></pre>
14029      */
14030     getSortState : function(){
14031         return this.sortInfo;
14032     },
14033
14034     // private
14035     applySort : function(){
14036         if(this.sortInfo && !this.remoteSort){
14037             var s = this.sortInfo, f = s.field;
14038             var st = this.fields.get(f).sortType;
14039             var fn = function(r1, r2){
14040                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14041                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14042             };
14043             this.data.sort(s.direction, fn);
14044             if(this.snapshot && this.snapshot != this.data){
14045                 this.snapshot.sort(s.direction, fn);
14046             }
14047         }
14048     },
14049
14050     /**
14051      * Sets the default sort column and order to be used by the next load operation.
14052      * @param {String} fieldName The name of the field to sort by.
14053      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14054      */
14055     setDefaultSort : function(field, dir){
14056         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14057     },
14058
14059     /**
14060      * Sort the Records.
14061      * If remote sorting is used, the sort is performed on the server, and the cache is
14062      * reloaded. If local sorting is used, the cache is sorted internally.
14063      * @param {String} fieldName The name of the field to sort by.
14064      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14065      */
14066     sort : function(fieldName, dir){
14067         var f = this.fields.get(fieldName);
14068         if(!dir){
14069             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14070             
14071             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14072                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14073             }else{
14074                 dir = f.sortDir;
14075             }
14076         }
14077         this.sortToggle[f.name] = dir;
14078         this.sortInfo = {field: f.name, direction: dir};
14079         if(!this.remoteSort){
14080             this.applySort();
14081             this.fireEvent("datachanged", this);
14082         }else{
14083             this.load(this.lastOptions);
14084         }
14085     },
14086
14087     /**
14088      * Calls the specified function for each of the Records in the cache.
14089      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14090      * Returning <em>false</em> aborts and exits the iteration.
14091      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14092      */
14093     each : function(fn, scope){
14094         this.data.each(fn, scope);
14095     },
14096
14097     /**
14098      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14099      * (e.g., during paging).
14100      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14101      */
14102     getModifiedRecords : function(){
14103         return this.modified;
14104     },
14105
14106     // private
14107     createFilterFn : function(property, value, anyMatch){
14108         if(!value.exec){ // not a regex
14109             value = String(value);
14110             if(value.length == 0){
14111                 return false;
14112             }
14113             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14114         }
14115         return function(r){
14116             return value.test(r.data[property]);
14117         };
14118     },
14119
14120     /**
14121      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14122      * @param {String} property A field on your records
14123      * @param {Number} start The record index to start at (defaults to 0)
14124      * @param {Number} end The last record index to include (defaults to length - 1)
14125      * @return {Number} The sum
14126      */
14127     sum : function(property, start, end){
14128         var rs = this.data.items, v = 0;
14129         start = start || 0;
14130         end = (end || end === 0) ? end : rs.length-1;
14131
14132         for(var i = start; i <= end; i++){
14133             v += (rs[i].data[property] || 0);
14134         }
14135         return v;
14136     },
14137
14138     /**
14139      * Filter the records by a specified property.
14140      * @param {String} field A field on your records
14141      * @param {String/RegExp} value Either a string that the field
14142      * should start with or a RegExp to test against the field
14143      * @param {Boolean} anyMatch True to match any part not just the beginning
14144      */
14145     filter : function(property, value, anyMatch){
14146         var fn = this.createFilterFn(property, value, anyMatch);
14147         return fn ? this.filterBy(fn) : this.clearFilter();
14148     },
14149
14150     /**
14151      * Filter by a function. The specified function will be called with each
14152      * record in this data source. If the function returns true the record is included,
14153      * otherwise it is filtered.
14154      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14155      * @param {Object} scope (optional) The scope of the function (defaults to this)
14156      */
14157     filterBy : function(fn, scope){
14158         this.snapshot = this.snapshot || this.data;
14159         this.data = this.queryBy(fn, scope||this);
14160         this.fireEvent("datachanged", this);
14161     },
14162
14163     /**
14164      * Query the records by a specified property.
14165      * @param {String} field A field on your records
14166      * @param {String/RegExp} value Either a string that the field
14167      * should start with or a RegExp to test against the field
14168      * @param {Boolean} anyMatch True to match any part not just the beginning
14169      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14170      */
14171     query : function(property, value, anyMatch){
14172         var fn = this.createFilterFn(property, value, anyMatch);
14173         return fn ? this.queryBy(fn) : this.data.clone();
14174     },
14175
14176     /**
14177      * Query by a function. The specified function will be called with each
14178      * record in this data source. If the function returns true the record is included
14179      * in the results.
14180      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14181      * @param {Object} scope (optional) The scope of the function (defaults to this)
14182       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14183      **/
14184     queryBy : function(fn, scope){
14185         var data = this.snapshot || this.data;
14186         return data.filterBy(fn, scope||this);
14187     },
14188
14189     /**
14190      * Collects unique values for a particular dataIndex from this store.
14191      * @param {String} dataIndex The property to collect
14192      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14193      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14194      * @return {Array} An array of the unique values
14195      **/
14196     collect : function(dataIndex, allowNull, bypassFilter){
14197         var d = (bypassFilter === true && this.snapshot) ?
14198                 this.snapshot.items : this.data.items;
14199         var v, sv, r = [], l = {};
14200         for(var i = 0, len = d.length; i < len; i++){
14201             v = d[i].data[dataIndex];
14202             sv = String(v);
14203             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14204                 l[sv] = true;
14205                 r[r.length] = v;
14206             }
14207         }
14208         return r;
14209     },
14210
14211     /**
14212      * Revert to a view of the Record cache with no filtering applied.
14213      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14214      */
14215     clearFilter : function(suppressEvent){
14216         if(this.snapshot && this.snapshot != this.data){
14217             this.data = this.snapshot;
14218             delete this.snapshot;
14219             if(suppressEvent !== true){
14220                 this.fireEvent("datachanged", this);
14221             }
14222         }
14223     },
14224
14225     // private
14226     afterEdit : function(record){
14227         if(this.modified.indexOf(record) == -1){
14228             this.modified.push(record);
14229         }
14230         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14231     },
14232     
14233     // private
14234     afterReject : function(record){
14235         this.modified.remove(record);
14236         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14237     },
14238
14239     // private
14240     afterCommit : function(record){
14241         this.modified.remove(record);
14242         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14243     },
14244
14245     /**
14246      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14247      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14248      */
14249     commitChanges : function(){
14250         var m = this.modified.slice(0);
14251         this.modified = [];
14252         for(var i = 0, len = m.length; i < len; i++){
14253             m[i].commit();
14254         }
14255     },
14256
14257     /**
14258      * Cancel outstanding changes on all changed records.
14259      */
14260     rejectChanges : function(){
14261         var m = this.modified.slice(0);
14262         this.modified = [];
14263         for(var i = 0, len = m.length; i < len; i++){
14264             m[i].reject();
14265         }
14266     },
14267
14268     onMetaChange : function(meta, rtype, o){
14269         this.recordType = rtype;
14270         this.fields = rtype.prototype.fields;
14271         delete this.snapshot;
14272         this.sortInfo = meta.sortInfo || this.sortInfo;
14273         this.modified = [];
14274         this.fireEvent('metachange', this, this.reader.meta);
14275     },
14276     
14277     moveIndex : function(data, type)
14278     {
14279         var index = this.indexOf(data);
14280         
14281         var newIndex = index + type;
14282         
14283         this.remove(data);
14284         
14285         this.insert(newIndex, data);
14286         
14287     }
14288 });/*
14289  * Based on:
14290  * Ext JS Library 1.1.1
14291  * Copyright(c) 2006-2007, Ext JS, LLC.
14292  *
14293  * Originally Released Under LGPL - original licence link has changed is not relivant.
14294  *
14295  * Fork - LGPL
14296  * <script type="text/javascript">
14297  */
14298
14299 /**
14300  * @class Roo.data.SimpleStore
14301  * @extends Roo.data.Store
14302  * Small helper class to make creating Stores from Array data easier.
14303  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14304  * @cfg {Array} fields An array of field definition objects, or field name strings.
14305  * @cfg {Object} an existing reader (eg. copied from another store)
14306  * @cfg {Array} data The multi-dimensional array of data
14307  * @constructor
14308  * @param {Object} config
14309  */
14310 Roo.data.SimpleStore = function(config)
14311 {
14312     Roo.data.SimpleStore.superclass.constructor.call(this, {
14313         isLocal : true,
14314         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14315                 id: config.id
14316             },
14317             Roo.data.Record.create(config.fields)
14318         ),
14319         proxy : new Roo.data.MemoryProxy(config.data)
14320     });
14321     this.load();
14322 };
14323 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14324  * Based on:
14325  * Ext JS Library 1.1.1
14326  * Copyright(c) 2006-2007, Ext JS, LLC.
14327  *
14328  * Originally Released Under LGPL - original licence link has changed is not relivant.
14329  *
14330  * Fork - LGPL
14331  * <script type="text/javascript">
14332  */
14333
14334 /**
14335 /**
14336  * @extends Roo.data.Store
14337  * @class Roo.data.JsonStore
14338  * Small helper class to make creating Stores for JSON data easier. <br/>
14339 <pre><code>
14340 var store = new Roo.data.JsonStore({
14341     url: 'get-images.php',
14342     root: 'images',
14343     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14344 });
14345 </code></pre>
14346  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14347  * JsonReader and HttpProxy (unless inline data is provided).</b>
14348  * @cfg {Array} fields An array of field definition objects, or field name strings.
14349  * @constructor
14350  * @param {Object} config
14351  */
14352 Roo.data.JsonStore = function(c){
14353     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14354         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14355         reader: new Roo.data.JsonReader(c, c.fields)
14356     }));
14357 };
14358 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14359  * Based on:
14360  * Ext JS Library 1.1.1
14361  * Copyright(c) 2006-2007, Ext JS, LLC.
14362  *
14363  * Originally Released Under LGPL - original licence link has changed is not relivant.
14364  *
14365  * Fork - LGPL
14366  * <script type="text/javascript">
14367  */
14368
14369  
14370 Roo.data.Field = function(config){
14371     if(typeof config == "string"){
14372         config = {name: config};
14373     }
14374     Roo.apply(this, config);
14375     
14376     if(!this.type){
14377         this.type = "auto";
14378     }
14379     
14380     var st = Roo.data.SortTypes;
14381     // named sortTypes are supported, here we look them up
14382     if(typeof this.sortType == "string"){
14383         this.sortType = st[this.sortType];
14384     }
14385     
14386     // set default sortType for strings and dates
14387     if(!this.sortType){
14388         switch(this.type){
14389             case "string":
14390                 this.sortType = st.asUCString;
14391                 break;
14392             case "date":
14393                 this.sortType = st.asDate;
14394                 break;
14395             default:
14396                 this.sortType = st.none;
14397         }
14398     }
14399
14400     // define once
14401     var stripRe = /[\$,%]/g;
14402
14403     // prebuilt conversion function for this field, instead of
14404     // switching every time we're reading a value
14405     if(!this.convert){
14406         var cv, dateFormat = this.dateFormat;
14407         switch(this.type){
14408             case "":
14409             case "auto":
14410             case undefined:
14411                 cv = function(v){ return v; };
14412                 break;
14413             case "string":
14414                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14415                 break;
14416             case "int":
14417                 cv = function(v){
14418                     return v !== undefined && v !== null && v !== '' ?
14419                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14420                     };
14421                 break;
14422             case "float":
14423                 cv = function(v){
14424                     return v !== undefined && v !== null && v !== '' ?
14425                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14426                     };
14427                 break;
14428             case "bool":
14429             case "boolean":
14430                 cv = function(v){ return v === true || v === "true" || v == 1; };
14431                 break;
14432             case "date":
14433                 cv = function(v){
14434                     if(!v){
14435                         return '';
14436                     }
14437                     if(v instanceof Date){
14438                         return v;
14439                     }
14440                     if(dateFormat){
14441                         if(dateFormat == "timestamp"){
14442                             return new Date(v*1000);
14443                         }
14444                         return Date.parseDate(v, dateFormat);
14445                     }
14446                     var parsed = Date.parse(v);
14447                     return parsed ? new Date(parsed) : null;
14448                 };
14449              break;
14450             
14451         }
14452         this.convert = cv;
14453     }
14454 };
14455
14456 Roo.data.Field.prototype = {
14457     dateFormat: null,
14458     defaultValue: "",
14459     mapping: null,
14460     sortType : null,
14461     sortDir : "ASC"
14462 };/*
14463  * Based on:
14464  * Ext JS Library 1.1.1
14465  * Copyright(c) 2006-2007, Ext JS, LLC.
14466  *
14467  * Originally Released Under LGPL - original licence link has changed is not relivant.
14468  *
14469  * Fork - LGPL
14470  * <script type="text/javascript">
14471  */
14472  
14473 // Base class for reading structured data from a data source.  This class is intended to be
14474 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14475
14476 /**
14477  * @class Roo.data.DataReader
14478  * Base class for reading structured data from a data source.  This class is intended to be
14479  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14480  */
14481
14482 Roo.data.DataReader = function(meta, recordType){
14483     
14484     this.meta = meta;
14485     
14486     this.recordType = recordType instanceof Array ? 
14487         Roo.data.Record.create(recordType) : recordType;
14488 };
14489
14490 Roo.data.DataReader.prototype = {
14491     
14492     
14493     readerType : 'Data',
14494      /**
14495      * Create an empty record
14496      * @param {Object} data (optional) - overlay some values
14497      * @return {Roo.data.Record} record created.
14498      */
14499     newRow :  function(d) {
14500         var da =  {};
14501         this.recordType.prototype.fields.each(function(c) {
14502             switch( c.type) {
14503                 case 'int' : da[c.name] = 0; break;
14504                 case 'date' : da[c.name] = new Date(); break;
14505                 case 'float' : da[c.name] = 0.0; break;
14506                 case 'boolean' : da[c.name] = false; break;
14507                 default : da[c.name] = ""; break;
14508             }
14509             
14510         });
14511         return new this.recordType(Roo.apply(da, d));
14512     }
14513     
14514     
14515 };/*
14516  * Based on:
14517  * Ext JS Library 1.1.1
14518  * Copyright(c) 2006-2007, Ext JS, LLC.
14519  *
14520  * Originally Released Under LGPL - original licence link has changed is not relivant.
14521  *
14522  * Fork - LGPL
14523  * <script type="text/javascript">
14524  */
14525
14526 /**
14527  * @class Roo.data.DataProxy
14528  * @extends Roo.data.Observable
14529  * This class is an abstract base class for implementations which provide retrieval of
14530  * unformatted data objects.<br>
14531  * <p>
14532  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14533  * (of the appropriate type which knows how to parse the data object) to provide a block of
14534  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14535  * <p>
14536  * Custom implementations must implement the load method as described in
14537  * {@link Roo.data.HttpProxy#load}.
14538  */
14539 Roo.data.DataProxy = function(){
14540     this.addEvents({
14541         /**
14542          * @event beforeload
14543          * Fires before a network request is made to retrieve a data object.
14544          * @param {Object} This DataProxy object.
14545          * @param {Object} params The params parameter to the load function.
14546          */
14547         beforeload : true,
14548         /**
14549          * @event load
14550          * Fires before the load method's callback is called.
14551          * @param {Object} This DataProxy object.
14552          * @param {Object} o The data object.
14553          * @param {Object} arg The callback argument object passed to the load function.
14554          */
14555         load : true,
14556         /**
14557          * @event loadexception
14558          * Fires if an Exception occurs during data retrieval.
14559          * @param {Object} This DataProxy object.
14560          * @param {Object} o The data object.
14561          * @param {Object} arg The callback argument object passed to the load function.
14562          * @param {Object} e The Exception.
14563          */
14564         loadexception : true
14565     });
14566     Roo.data.DataProxy.superclass.constructor.call(this);
14567 };
14568
14569 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14570
14571     /**
14572      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14573      */
14574 /*
14575  * Based on:
14576  * Ext JS Library 1.1.1
14577  * Copyright(c) 2006-2007, Ext JS, LLC.
14578  *
14579  * Originally Released Under LGPL - original licence link has changed is not relivant.
14580  *
14581  * Fork - LGPL
14582  * <script type="text/javascript">
14583  */
14584 /**
14585  * @class Roo.data.MemoryProxy
14586  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14587  * to the Reader when its load method is called.
14588  * @constructor
14589  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14590  */
14591 Roo.data.MemoryProxy = function(data){
14592     if (data.data) {
14593         data = data.data;
14594     }
14595     Roo.data.MemoryProxy.superclass.constructor.call(this);
14596     this.data = data;
14597 };
14598
14599 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14600     
14601     /**
14602      * Load data from the requested source (in this case an in-memory
14603      * data object passed to the constructor), read the data object into
14604      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14605      * process that block using the passed callback.
14606      * @param {Object} params This parameter is not used by the MemoryProxy class.
14607      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14608      * object into a block of Roo.data.Records.
14609      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14610      * The function must be passed <ul>
14611      * <li>The Record block object</li>
14612      * <li>The "arg" argument from the load function</li>
14613      * <li>A boolean success indicator</li>
14614      * </ul>
14615      * @param {Object} scope The scope in which to call the callback
14616      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14617      */
14618     load : function(params, reader, callback, scope, arg){
14619         params = params || {};
14620         var result;
14621         try {
14622             result = reader.readRecords(params.data ? params.data :this.data);
14623         }catch(e){
14624             this.fireEvent("loadexception", this, arg, null, e);
14625             callback.call(scope, null, arg, false);
14626             return;
14627         }
14628         callback.call(scope, result, arg, true);
14629     },
14630     
14631     // private
14632     update : function(params, records){
14633         
14634     }
14635 });/*
14636  * Based on:
14637  * Ext JS Library 1.1.1
14638  * Copyright(c) 2006-2007, Ext JS, LLC.
14639  *
14640  * Originally Released Under LGPL - original licence link has changed is not relivant.
14641  *
14642  * Fork - LGPL
14643  * <script type="text/javascript">
14644  */
14645 /**
14646  * @class Roo.data.HttpProxy
14647  * @extends Roo.data.DataProxy
14648  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14649  * configured to reference a certain URL.<br><br>
14650  * <p>
14651  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14652  * from which the running page was served.<br><br>
14653  * <p>
14654  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14655  * <p>
14656  * Be aware that to enable the browser to parse an XML document, the server must set
14657  * the Content-Type header in the HTTP response to "text/xml".
14658  * @constructor
14659  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14660  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14661  * will be used to make the request.
14662  */
14663 Roo.data.HttpProxy = function(conn){
14664     Roo.data.HttpProxy.superclass.constructor.call(this);
14665     // is conn a conn config or a real conn?
14666     this.conn = conn;
14667     this.useAjax = !conn || !conn.events;
14668   
14669 };
14670
14671 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14672     // thse are take from connection...
14673     
14674     /**
14675      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14676      */
14677     /**
14678      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14679      * extra parameters to each request made by this object. (defaults to undefined)
14680      */
14681     /**
14682      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14683      *  to each request made by this object. (defaults to undefined)
14684      */
14685     /**
14686      * @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)
14687      */
14688     /**
14689      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14690      */
14691      /**
14692      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14693      * @type Boolean
14694      */
14695   
14696
14697     /**
14698      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14699      * @type Boolean
14700      */
14701     /**
14702      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14703      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14704      * a finer-grained basis than the DataProxy events.
14705      */
14706     getConnection : function(){
14707         return this.useAjax ? Roo.Ajax : this.conn;
14708     },
14709
14710     /**
14711      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14712      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14713      * process that block using the passed callback.
14714      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14715      * for the request to the remote server.
14716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14717      * object into a block of Roo.data.Records.
14718      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14719      * The function must be passed <ul>
14720      * <li>The Record block object</li>
14721      * <li>The "arg" argument from the load function</li>
14722      * <li>A boolean success indicator</li>
14723      * </ul>
14724      * @param {Object} scope The scope in which to call the callback
14725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14726      */
14727     load : function(params, reader, callback, scope, arg){
14728         if(this.fireEvent("beforeload", this, params) !== false){
14729             var  o = {
14730                 params : params || {},
14731                 request: {
14732                     callback : callback,
14733                     scope : scope,
14734                     arg : arg
14735                 },
14736                 reader: reader,
14737                 callback : this.loadResponse,
14738                 scope: this
14739             };
14740             if(this.useAjax){
14741                 Roo.applyIf(o, this.conn);
14742                 if(this.activeRequest){
14743                     Roo.Ajax.abort(this.activeRequest);
14744                 }
14745                 this.activeRequest = Roo.Ajax.request(o);
14746             }else{
14747                 this.conn.request(o);
14748             }
14749         }else{
14750             callback.call(scope||this, null, arg, false);
14751         }
14752     },
14753
14754     // private
14755     loadResponse : function(o, success, response){
14756         delete this.activeRequest;
14757         if(!success){
14758             this.fireEvent("loadexception", this, o, response);
14759             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14760             return;
14761         }
14762         var result;
14763         try {
14764             result = o.reader.read(response);
14765         }catch(e){
14766             this.fireEvent("loadexception", this, o, response, e);
14767             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14768             return;
14769         }
14770         
14771         this.fireEvent("load", this, o, o.request.arg);
14772         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14773     },
14774
14775     // private
14776     update : function(dataSet){
14777
14778     },
14779
14780     // private
14781     updateResponse : function(dataSet){
14782
14783     }
14784 });/*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794
14795 /**
14796  * @class Roo.data.ScriptTagProxy
14797  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14798  * other than the originating domain of the running page.<br><br>
14799  * <p>
14800  * <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
14801  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14802  * <p>
14803  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14804  * source code that is used as the source inside a &lt;script> tag.<br><br>
14805  * <p>
14806  * In order for the browser to process the returned data, the server must wrap the data object
14807  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14808  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14809  * depending on whether the callback name was passed:
14810  * <p>
14811  * <pre><code>
14812 boolean scriptTag = false;
14813 String cb = request.getParameter("callback");
14814 if (cb != null) {
14815     scriptTag = true;
14816     response.setContentType("text/javascript");
14817 } else {
14818     response.setContentType("application/x-json");
14819 }
14820 Writer out = response.getWriter();
14821 if (scriptTag) {
14822     out.write(cb + "(");
14823 }
14824 out.print(dataBlock.toJsonString());
14825 if (scriptTag) {
14826     out.write(");");
14827 }
14828 </pre></code>
14829  *
14830  * @constructor
14831  * @param {Object} config A configuration object.
14832  */
14833 Roo.data.ScriptTagProxy = function(config){
14834     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14835     Roo.apply(this, config);
14836     this.head = document.getElementsByTagName("head")[0];
14837 };
14838
14839 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14840
14841 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14842     /**
14843      * @cfg {String} url The URL from which to request the data object.
14844      */
14845     /**
14846      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14847      */
14848     timeout : 30000,
14849     /**
14850      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14851      * the server the name of the callback function set up by the load call to process the returned data object.
14852      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14853      * javascript output which calls this named function passing the data object as its only parameter.
14854      */
14855     callbackParam : "callback",
14856     /**
14857      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14858      * name to the request.
14859      */
14860     nocache : true,
14861
14862     /**
14863      * Load data from the configured URL, read the data object into
14864      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14865      * process that block using the passed callback.
14866      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14867      * for the request to the remote server.
14868      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14869      * object into a block of Roo.data.Records.
14870      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14871      * The function must be passed <ul>
14872      * <li>The Record block object</li>
14873      * <li>The "arg" argument from the load function</li>
14874      * <li>A boolean success indicator</li>
14875      * </ul>
14876      * @param {Object} scope The scope in which to call the callback
14877      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14878      */
14879     load : function(params, reader, callback, scope, arg){
14880         if(this.fireEvent("beforeload", this, params) !== false){
14881
14882             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14883
14884             var url = this.url;
14885             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14886             if(this.nocache){
14887                 url += "&_dc=" + (new Date().getTime());
14888             }
14889             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14890             var trans = {
14891                 id : transId,
14892                 cb : "stcCallback"+transId,
14893                 scriptId : "stcScript"+transId,
14894                 params : params,
14895                 arg : arg,
14896                 url : url,
14897                 callback : callback,
14898                 scope : scope,
14899                 reader : reader
14900             };
14901             var conn = this;
14902
14903             window[trans.cb] = function(o){
14904                 conn.handleResponse(o, trans);
14905             };
14906
14907             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14908
14909             if(this.autoAbort !== false){
14910                 this.abort();
14911             }
14912
14913             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14914
14915             var script = document.createElement("script");
14916             script.setAttribute("src", url);
14917             script.setAttribute("type", "text/javascript");
14918             script.setAttribute("id", trans.scriptId);
14919             this.head.appendChild(script);
14920
14921             this.trans = trans;
14922         }else{
14923             callback.call(scope||this, null, arg, false);
14924         }
14925     },
14926
14927     // private
14928     isLoading : function(){
14929         return this.trans ? true : false;
14930     },
14931
14932     /**
14933      * Abort the current server request.
14934      */
14935     abort : function(){
14936         if(this.isLoading()){
14937             this.destroyTrans(this.trans);
14938         }
14939     },
14940
14941     // private
14942     destroyTrans : function(trans, isLoaded){
14943         this.head.removeChild(document.getElementById(trans.scriptId));
14944         clearTimeout(trans.timeoutId);
14945         if(isLoaded){
14946             window[trans.cb] = undefined;
14947             try{
14948                 delete window[trans.cb];
14949             }catch(e){}
14950         }else{
14951             // if hasn't been loaded, wait for load to remove it to prevent script error
14952             window[trans.cb] = function(){
14953                 window[trans.cb] = undefined;
14954                 try{
14955                     delete window[trans.cb];
14956                 }catch(e){}
14957             };
14958         }
14959     },
14960
14961     // private
14962     handleResponse : function(o, trans){
14963         this.trans = false;
14964         this.destroyTrans(trans, true);
14965         var result;
14966         try {
14967             result = trans.reader.readRecords(o);
14968         }catch(e){
14969             this.fireEvent("loadexception", this, o, trans.arg, e);
14970             trans.callback.call(trans.scope||window, null, trans.arg, false);
14971             return;
14972         }
14973         this.fireEvent("load", this, o, trans.arg);
14974         trans.callback.call(trans.scope||window, result, trans.arg, true);
14975     },
14976
14977     // private
14978     handleFailure : function(trans){
14979         this.trans = false;
14980         this.destroyTrans(trans, false);
14981         this.fireEvent("loadexception", this, null, trans.arg);
14982         trans.callback.call(trans.scope||window, null, trans.arg, false);
14983     }
14984 });/*
14985  * Based on:
14986  * Ext JS Library 1.1.1
14987  * Copyright(c) 2006-2007, Ext JS, LLC.
14988  *
14989  * Originally Released Under LGPL - original licence link has changed is not relivant.
14990  *
14991  * Fork - LGPL
14992  * <script type="text/javascript">
14993  */
14994
14995 /**
14996  * @class Roo.data.JsonReader
14997  * @extends Roo.data.DataReader
14998  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14999  * based on mappings in a provided Roo.data.Record constructor.
15000  * 
15001  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15002  * in the reply previously. 
15003  * 
15004  * <p>
15005  * Example code:
15006  * <pre><code>
15007 var RecordDef = Roo.data.Record.create([
15008     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15009     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15010 ]);
15011 var myReader = new Roo.data.JsonReader({
15012     totalProperty: "results",    // The property which contains the total dataset size (optional)
15013     root: "rows",                // The property which contains an Array of row objects
15014     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15015 }, RecordDef);
15016 </code></pre>
15017  * <p>
15018  * This would consume a JSON file like this:
15019  * <pre><code>
15020 { 'results': 2, 'rows': [
15021     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15022     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15023 }
15024 </code></pre>
15025  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15026  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15027  * paged from the remote server.
15028  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15029  * @cfg {String} root name of the property which contains the Array of row objects.
15030  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15031  * @cfg {Array} fields Array of field definition objects
15032  * @constructor
15033  * Create a new JsonReader
15034  * @param {Object} meta Metadata configuration options
15035  * @param {Object} recordType Either an Array of field definition objects,
15036  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15037  */
15038 Roo.data.JsonReader = function(meta, recordType){
15039     
15040     meta = meta || {};
15041     // set some defaults:
15042     Roo.applyIf(meta, {
15043         totalProperty: 'total',
15044         successProperty : 'success',
15045         root : 'data',
15046         id : 'id'
15047     });
15048     
15049     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15050 };
15051 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15052     
15053     readerType : 'Json',
15054     
15055     /**
15056      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15057      * Used by Store query builder to append _requestMeta to params.
15058      * 
15059      */
15060     metaFromRemote : false,
15061     /**
15062      * This method is only used by a DataProxy which has retrieved data from a remote server.
15063      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15064      * @return {Object} data A data block which is used by an Roo.data.Store object as
15065      * a cache of Roo.data.Records.
15066      */
15067     read : function(response){
15068         var json = response.responseText;
15069        
15070         var o = /* eval:var:o */ eval("("+json+")");
15071         if(!o) {
15072             throw {message: "JsonReader.read: Json object not found"};
15073         }
15074         
15075         if(o.metaData){
15076             
15077             delete this.ef;
15078             this.metaFromRemote = true;
15079             this.meta = o.metaData;
15080             this.recordType = Roo.data.Record.create(o.metaData.fields);
15081             this.onMetaChange(this.meta, this.recordType, o);
15082         }
15083         return this.readRecords(o);
15084     },
15085
15086     // private function a store will implement
15087     onMetaChange : function(meta, recordType, o){
15088
15089     },
15090
15091     /**
15092          * @ignore
15093          */
15094     simpleAccess: function(obj, subsc) {
15095         return obj[subsc];
15096     },
15097
15098         /**
15099          * @ignore
15100          */
15101     getJsonAccessor: function(){
15102         var re = /[\[\.]/;
15103         return function(expr) {
15104             try {
15105                 return(re.test(expr))
15106                     ? new Function("obj", "return obj." + expr)
15107                     : function(obj){
15108                         return obj[expr];
15109                     };
15110             } catch(e){}
15111             return Roo.emptyFn;
15112         };
15113     }(),
15114
15115     /**
15116      * Create a data block containing Roo.data.Records from an XML document.
15117      * @param {Object} o An object which contains an Array of row objects in the property specified
15118      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15119      * which contains the total size of the dataset.
15120      * @return {Object} data A data block which is used by an Roo.data.Store object as
15121      * a cache of Roo.data.Records.
15122      */
15123     readRecords : function(o){
15124         /**
15125          * After any data loads, the raw JSON data is available for further custom processing.
15126          * @type Object
15127          */
15128         this.o = o;
15129         var s = this.meta, Record = this.recordType,
15130             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15131
15132 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15133         if (!this.ef) {
15134             if(s.totalProperty) {
15135                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15136                 }
15137                 if(s.successProperty) {
15138                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15139                 }
15140                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15141                 if (s.id) {
15142                         var g = this.getJsonAccessor(s.id);
15143                         this.getId = function(rec) {
15144                                 var r = g(rec);  
15145                                 return (r === undefined || r === "") ? null : r;
15146                         };
15147                 } else {
15148                         this.getId = function(){return null;};
15149                 }
15150             this.ef = [];
15151             for(var jj = 0; jj < fl; jj++){
15152                 f = fi[jj];
15153                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15154                 this.ef[jj] = this.getJsonAccessor(map);
15155             }
15156         }
15157
15158         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15159         if(s.totalProperty){
15160             var vt = parseInt(this.getTotal(o), 10);
15161             if(!isNaN(vt)){
15162                 totalRecords = vt;
15163             }
15164         }
15165         if(s.successProperty){
15166             var vs = this.getSuccess(o);
15167             if(vs === false || vs === 'false'){
15168                 success = false;
15169             }
15170         }
15171         var records = [];
15172         for(var i = 0; i < c; i++){
15173                 var n = root[i];
15174             var values = {};
15175             var id = this.getId(n);
15176             for(var j = 0; j < fl; j++){
15177                 f = fi[j];
15178             var v = this.ef[j](n);
15179             if (!f.convert) {
15180                 Roo.log('missing convert for ' + f.name);
15181                 Roo.log(f);
15182                 continue;
15183             }
15184             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15185             }
15186             var record = new Record(values, id);
15187             record.json = n;
15188             records[i] = record;
15189         }
15190         return {
15191             raw : o,
15192             success : success,
15193             records : records,
15194             totalRecords : totalRecords
15195         };
15196     },
15197     // used when loading children.. @see loadDataFromChildren
15198     toLoadData: function(rec)
15199     {
15200         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15201         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15202         return { data : data, total : data.length };
15203         
15204     }
15205 });/*
15206  * Based on:
15207  * Ext JS Library 1.1.1
15208  * Copyright(c) 2006-2007, Ext JS, LLC.
15209  *
15210  * Originally Released Under LGPL - original licence link has changed is not relivant.
15211  *
15212  * Fork - LGPL
15213  * <script type="text/javascript">
15214  */
15215
15216 /**
15217  * @class Roo.data.ArrayReader
15218  * @extends Roo.data.DataReader
15219  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15220  * Each element of that Array represents a row of data fields. The
15221  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15222  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15223  * <p>
15224  * Example code:.
15225  * <pre><code>
15226 var RecordDef = Roo.data.Record.create([
15227     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15228     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15229 ]);
15230 var myReader = new Roo.data.ArrayReader({
15231     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15232 }, RecordDef);
15233 </code></pre>
15234  * <p>
15235  * This would consume an Array like this:
15236  * <pre><code>
15237 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15238   </code></pre>
15239  
15240  * @constructor
15241  * Create a new JsonReader
15242  * @param {Object} meta Metadata configuration options.
15243  * @param {Object|Array} recordType Either an Array of field definition objects
15244  * 
15245  * @cfg {Array} fields Array of field definition objects
15246  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15247  * as specified to {@link Roo.data.Record#create},
15248  * or an {@link Roo.data.Record} object
15249  *
15250  * 
15251  * created using {@link Roo.data.Record#create}.
15252  */
15253 Roo.data.ArrayReader = function(meta, recordType)
15254 {    
15255     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15256 };
15257
15258 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15259     
15260       /**
15261      * Create a data block containing Roo.data.Records from an XML document.
15262      * @param {Object} o An Array of row objects which represents the dataset.
15263      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15264      * a cache of Roo.data.Records.
15265      */
15266     readRecords : function(o)
15267     {
15268         var sid = this.meta ? this.meta.id : null;
15269         var recordType = this.recordType, fields = recordType.prototype.fields;
15270         var records = [];
15271         var root = o;
15272         for(var i = 0; i < root.length; i++){
15273             var n = root[i];
15274             var values = {};
15275             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15276             for(var j = 0, jlen = fields.length; j < jlen; j++){
15277                 var f = fields.items[j];
15278                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15279                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15280                 v = f.convert(v);
15281                 values[f.name] = v;
15282             }
15283             var record = new recordType(values, id);
15284             record.json = n;
15285             records[records.length] = record;
15286         }
15287         return {
15288             records : records,
15289             totalRecords : records.length
15290         };
15291     },
15292     // used when loading children.. @see loadDataFromChildren
15293     toLoadData: function(rec)
15294     {
15295         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15296         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15297         
15298     }
15299     
15300     
15301 });/*
15302  * - LGPL
15303  * * 
15304  */
15305
15306 /**
15307  * @class Roo.bootstrap.ComboBox
15308  * @extends Roo.bootstrap.TriggerField
15309  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15310  * @cfg {Boolean} append (true|false) default false
15311  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15312  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15313  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15314  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15315  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15316  * @cfg {Boolean} animate default true
15317  * @cfg {Boolean} emptyResultText only for touch device
15318  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15319  * @cfg {String} emptyTitle default ''
15320  * @cfg {Number} width fixed with? experimental
15321  * @constructor
15322  * Create a new ComboBox.
15323  * @param {Object} config Configuration options
15324  */
15325 Roo.bootstrap.ComboBox = function(config){
15326     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15327     this.addEvents({
15328         /**
15329          * @event expand
15330          * Fires when the dropdown list is expanded
15331         * @param {Roo.bootstrap.ComboBox} combo This combo box
15332         */
15333         'expand' : true,
15334         /**
15335          * @event collapse
15336          * Fires when the dropdown list is collapsed
15337         * @param {Roo.bootstrap.ComboBox} combo This combo box
15338         */
15339         'collapse' : true,
15340         /**
15341          * @event beforeselect
15342          * Fires before a list item is selected. Return false to cancel the selection.
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         * @param {Roo.data.Record} record The data record returned from the underlying store
15345         * @param {Number} index The index of the selected item in the dropdown list
15346         */
15347         'beforeselect' : true,
15348         /**
15349          * @event select
15350          * Fires when a list item is selected
15351         * @param {Roo.bootstrap.ComboBox} combo This combo box
15352         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15353         * @param {Number} index The index of the selected item in the dropdown list
15354         */
15355         'select' : true,
15356         /**
15357          * @event beforequery
15358          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15359          * The event object passed has these properties:
15360         * @param {Roo.bootstrap.ComboBox} combo This combo box
15361         * @param {String} query The query
15362         * @param {Boolean} forceAll true to force "all" query
15363         * @param {Boolean} cancel true to cancel the query
15364         * @param {Object} e The query event object
15365         */
15366         'beforequery': true,
15367          /**
15368          * @event add
15369          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15370         * @param {Roo.bootstrap.ComboBox} combo This combo box
15371         */
15372         'add' : true,
15373         /**
15374          * @event edit
15375          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15376         * @param {Roo.bootstrap.ComboBox} combo This combo box
15377         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15378         */
15379         'edit' : true,
15380         /**
15381          * @event remove
15382          * Fires when the remove value from the combobox array
15383         * @param {Roo.bootstrap.ComboBox} combo This combo box
15384         */
15385         'remove' : true,
15386         /**
15387          * @event afterremove
15388          * Fires when the remove value from the combobox array
15389         * @param {Roo.bootstrap.ComboBox} combo This combo box
15390         */
15391         'afterremove' : true,
15392         /**
15393          * @event specialfilter
15394          * Fires when specialfilter
15395             * @param {Roo.bootstrap.ComboBox} combo This combo box
15396             */
15397         'specialfilter' : true,
15398         /**
15399          * @event tick
15400          * Fires when tick the element
15401             * @param {Roo.bootstrap.ComboBox} combo This combo box
15402             */
15403         'tick' : true,
15404         /**
15405          * @event touchviewdisplay
15406          * Fires when touch view require special display (default is using displayField)
15407             * @param {Roo.bootstrap.ComboBox} combo This combo box
15408             * @param {Object} cfg set html .
15409             */
15410         'touchviewdisplay' : true
15411         
15412     });
15413     
15414     this.item = [];
15415     this.tickItems = [];
15416     
15417     this.selectedIndex = -1;
15418     if(this.mode == 'local'){
15419         if(config.queryDelay === undefined){
15420             this.queryDelay = 10;
15421         }
15422         if(config.minChars === undefined){
15423             this.minChars = 0;
15424         }
15425     }
15426 };
15427
15428 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15429      
15430     /**
15431      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15432      * rendering into an Roo.Editor, defaults to false)
15433      */
15434     /**
15435      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15436      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15437      */
15438     /**
15439      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15440      */
15441     /**
15442      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15443      * the dropdown list (defaults to undefined, with no header element)
15444      */
15445
15446      /**
15447      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15448      */
15449      
15450      /**
15451      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15452      */
15453     listWidth: undefined,
15454     /**
15455      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15456      * mode = 'remote' or 'text' if mode = 'local')
15457      */
15458     displayField: undefined,
15459     
15460     /**
15461      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15462      * mode = 'remote' or 'value' if mode = 'local'). 
15463      * Note: use of a valueField requires the user make a selection
15464      * in order for a value to be mapped.
15465      */
15466     valueField: undefined,
15467     /**
15468      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15469      */
15470     modalTitle : '',
15471     
15472     /**
15473      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15474      * field's data value (defaults to the underlying DOM element's name)
15475      */
15476     hiddenName: undefined,
15477     /**
15478      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15479      */
15480     listClass: '',
15481     /**
15482      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15483      */
15484     selectedClass: 'active',
15485     
15486     /**
15487      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15488      */
15489     shadow:'sides',
15490     /**
15491      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15492      * anchor positions (defaults to 'tl-bl')
15493      */
15494     listAlign: 'tl-bl?',
15495     /**
15496      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15497      */
15498     maxHeight: 300,
15499     /**
15500      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15501      * query specified by the allQuery config option (defaults to 'query')
15502      */
15503     triggerAction: 'query',
15504     /**
15505      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15506      * (defaults to 4, does not apply if editable = false)
15507      */
15508     minChars : 4,
15509     /**
15510      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15511      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15512      */
15513     typeAhead: false,
15514     /**
15515      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15516      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15517      */
15518     queryDelay: 500,
15519     /**
15520      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15521      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15522      */
15523     pageSize: 0,
15524     /**
15525      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15526      * when editable = true (defaults to false)
15527      */
15528     selectOnFocus:false,
15529     /**
15530      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15531      */
15532     queryParam: 'query',
15533     /**
15534      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15535      * when mode = 'remote' (defaults to 'Loading...')
15536      */
15537     loadingText: 'Loading...',
15538     /**
15539      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15540      */
15541     resizable: false,
15542     /**
15543      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15544      */
15545     handleHeight : 8,
15546     /**
15547      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15548      * traditional select (defaults to true)
15549      */
15550     editable: true,
15551     /**
15552      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15553      */
15554     allQuery: '',
15555     /**
15556      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15557      */
15558     mode: 'remote',
15559     /**
15560      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15561      * listWidth has a higher value)
15562      */
15563     minListWidth : 70,
15564     /**
15565      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15566      * allow the user to set arbitrary text into the field (defaults to false)
15567      */
15568     forceSelection:false,
15569     /**
15570      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15571      * if typeAhead = true (defaults to 250)
15572      */
15573     typeAheadDelay : 250,
15574     /**
15575      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15576      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15577      */
15578     valueNotFoundText : undefined,
15579     /**
15580      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15581      */
15582     blockFocus : false,
15583     
15584     /**
15585      * @cfg {Boolean} disableClear Disable showing of clear button.
15586      */
15587     disableClear : false,
15588     /**
15589      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15590      */
15591     alwaysQuery : false,
15592     
15593     /**
15594      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15595      */
15596     multiple : false,
15597     
15598     /**
15599      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15600      */
15601     invalidClass : "has-warning",
15602     
15603     /**
15604      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15605      */
15606     validClass : "has-success",
15607     
15608     /**
15609      * @cfg {Boolean} specialFilter (true|false) special filter default false
15610      */
15611     specialFilter : false,
15612     
15613     /**
15614      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15615      */
15616     mobileTouchView : true,
15617     
15618     /**
15619      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15620      */
15621     useNativeIOS : false,
15622     
15623     /**
15624      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15625      */
15626     mobile_restrict_height : false,
15627     
15628     ios_options : false,
15629     
15630     //private
15631     addicon : false,
15632     editicon: false,
15633     
15634     page: 0,
15635     hasQuery: false,
15636     append: false,
15637     loadNext: false,
15638     autoFocus : true,
15639     tickable : false,
15640     btnPosition : 'right',
15641     triggerList : true,
15642     showToggleBtn : true,
15643     animate : true,
15644     emptyResultText: 'Empty',
15645     triggerText : 'Select',
15646     emptyTitle : '',
15647     width : false,
15648     
15649     // element that contains real text value.. (when hidden is used..)
15650     
15651     getAutoCreate : function()
15652     {   
15653         var cfg = false;
15654         //render
15655         /*
15656          * Render classic select for iso
15657          */
15658         
15659         if(Roo.isIOS && this.useNativeIOS){
15660             cfg = this.getAutoCreateNativeIOS();
15661             return cfg;
15662         }
15663         
15664         /*
15665          * Touch Devices
15666          */
15667         
15668         if(Roo.isTouch && this.mobileTouchView){
15669             cfg = this.getAutoCreateTouchView();
15670             return cfg;;
15671         }
15672         
15673         /*
15674          *  Normal ComboBox
15675          */
15676         if(!this.tickable){
15677             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15678             return cfg;
15679         }
15680         
15681         /*
15682          *  ComboBox with tickable selections
15683          */
15684              
15685         var align = this.labelAlign || this.parentLabelAlign();
15686         
15687         cfg = {
15688             cls : 'form-group roo-combobox-tickable' //input-group
15689         };
15690         
15691         var btn_text_select = '';
15692         var btn_text_done = '';
15693         var btn_text_cancel = '';
15694         
15695         if (this.btn_text_show) {
15696             btn_text_select = 'Select';
15697             btn_text_done = 'Done';
15698             btn_text_cancel = 'Cancel'; 
15699         }
15700         
15701         var buttons = {
15702             tag : 'div',
15703             cls : 'tickable-buttons',
15704             cn : [
15705                 {
15706                     tag : 'button',
15707                     type : 'button',
15708                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15709                     //html : this.triggerText
15710                     html: btn_text_select
15711                 },
15712                 {
15713                     tag : 'button',
15714                     type : 'button',
15715                     name : 'ok',
15716                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15717                     //html : 'Done'
15718                     html: btn_text_done
15719                 },
15720                 {
15721                     tag : 'button',
15722                     type : 'button',
15723                     name : 'cancel',
15724                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15725                     //html : 'Cancel'
15726                     html: btn_text_cancel
15727                 }
15728             ]
15729         };
15730         
15731         if(this.editable){
15732             buttons.cn.unshift({
15733                 tag: 'input',
15734                 cls: 'roo-select2-search-field-input'
15735             });
15736         }
15737         
15738         var _this = this;
15739         
15740         Roo.each(buttons.cn, function(c){
15741             if (_this.size) {
15742                 c.cls += ' btn-' + _this.size;
15743             }
15744
15745             if (_this.disabled) {
15746                 c.disabled = true;
15747             }
15748         });
15749         
15750         var box = {
15751             tag: 'div',
15752             style : 'display: contents',
15753             cn: [
15754                 {
15755                     tag: 'input',
15756                     type : 'hidden',
15757                     cls: 'form-hidden-field'
15758                 },
15759                 {
15760                     tag: 'ul',
15761                     cls: 'roo-select2-choices',
15762                     cn:[
15763                         {
15764                             tag: 'li',
15765                             cls: 'roo-select2-search-field',
15766                             cn: [
15767                                 buttons
15768                             ]
15769                         }
15770                     ]
15771                 }
15772             ]
15773         };
15774         
15775         var combobox = {
15776             cls: 'roo-select2-container input-group roo-select2-container-multi',
15777             cn: [
15778                 
15779                 box
15780 //                {
15781 //                    tag: 'ul',
15782 //                    cls: 'typeahead typeahead-long dropdown-menu',
15783 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15784 //                }
15785             ]
15786         };
15787         
15788         if(this.hasFeedback && !this.allowBlank){
15789             
15790             var feedback = {
15791                 tag: 'span',
15792                 cls: 'glyphicon form-control-feedback'
15793             };
15794
15795             combobox.cn.push(feedback);
15796         }
15797         
15798         
15799         
15800         var indicator = {
15801             tag : 'i',
15802             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15803             tooltip : 'This field is required'
15804         };
15805         if (Roo.bootstrap.version == 4) {
15806             indicator = {
15807                 tag : 'i',
15808                 style : 'display:none'
15809             };
15810         }
15811         if (align ==='left' && this.fieldLabel.length) {
15812             
15813             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15814             
15815             cfg.cn = [
15816                 indicator,
15817                 {
15818                     tag: 'label',
15819                     'for' :  id,
15820                     cls : 'control-label col-form-label',
15821                     html : this.fieldLabel
15822
15823                 },
15824                 {
15825                     cls : "", 
15826                     cn: [
15827                         combobox
15828                     ]
15829                 }
15830
15831             ];
15832             
15833             var labelCfg = cfg.cn[1];
15834             var contentCfg = cfg.cn[2];
15835             
15836
15837             if(this.indicatorpos == 'right'){
15838                 
15839                 cfg.cn = [
15840                     {
15841                         tag: 'label',
15842                         'for' :  id,
15843                         cls : 'control-label col-form-label',
15844                         cn : [
15845                             {
15846                                 tag : 'span',
15847                                 html : this.fieldLabel
15848                             },
15849                             indicator
15850                         ]
15851                     },
15852                     {
15853                         cls : "",
15854                         cn: [
15855                             combobox
15856                         ]
15857                     }
15858
15859                 ];
15860                 
15861                 
15862                 
15863                 labelCfg = cfg.cn[0];
15864                 contentCfg = cfg.cn[1];
15865             
15866             }
15867             
15868             if(this.labelWidth > 12){
15869                 labelCfg.style = "width: " + this.labelWidth + 'px';
15870             }
15871             if(this.width * 1 > 0){
15872                 contentCfg.style = "width: " + this.width + 'px';
15873             }
15874             if(this.labelWidth < 13 && this.labelmd == 0){
15875                 this.labelmd = this.labelWidth;
15876             }
15877             
15878             if(this.labellg > 0){
15879                 labelCfg.cls += ' col-lg-' + this.labellg;
15880                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15881             }
15882             
15883             if(this.labelmd > 0){
15884                 labelCfg.cls += ' col-md-' + this.labelmd;
15885                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15886             }
15887             
15888             if(this.labelsm > 0){
15889                 labelCfg.cls += ' col-sm-' + this.labelsm;
15890                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15891             }
15892             
15893             if(this.labelxs > 0){
15894                 labelCfg.cls += ' col-xs-' + this.labelxs;
15895                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15896             }
15897                 
15898                 
15899         } else if ( this.fieldLabel.length) {
15900 //                Roo.log(" label");
15901                  cfg.cn = [
15902                    indicator,
15903                     {
15904                         tag: 'label',
15905                         //cls : 'input-group-addon',
15906                         html : this.fieldLabel
15907                     },
15908                     combobox
15909                 ];
15910                 
15911                 if(this.indicatorpos == 'right'){
15912                     cfg.cn = [
15913                         {
15914                             tag: 'label',
15915                             //cls : 'input-group-addon',
15916                             html : this.fieldLabel
15917                         },
15918                         indicator,
15919                         combobox
15920                     ];
15921                     
15922                 }
15923
15924         } else {
15925             
15926 //                Roo.log(" no label && no align");
15927                 cfg = combobox
15928                      
15929                 
15930         }
15931          
15932         var settings=this;
15933         ['xs','sm','md','lg'].map(function(size){
15934             if (settings[size]) {
15935                 cfg.cls += ' col-' + size + '-' + settings[size];
15936             }
15937         });
15938         
15939         return cfg;
15940         
15941     },
15942     
15943     _initEventsCalled : false,
15944     
15945     // private
15946     initEvents: function()
15947     {   
15948         if (this._initEventsCalled) { // as we call render... prevent looping...
15949             return;
15950         }
15951         this._initEventsCalled = true;
15952         
15953         if (!this.store) {
15954             throw "can not find store for combo";
15955         }
15956         
15957         this.indicator = this.indicatorEl();
15958         
15959         this.store = Roo.factory(this.store, Roo.data);
15960         this.store.parent = this;
15961         
15962         // if we are building from html. then this element is so complex, that we can not really
15963         // use the rendered HTML.
15964         // so we have to trash and replace the previous code.
15965         if (Roo.XComponent.build_from_html) {
15966             // remove this element....
15967             var e = this.el.dom, k=0;
15968             while (e ) { e = e.previousSibling;  ++k;}
15969
15970             this.el.remove();
15971             
15972             this.el=false;
15973             this.rendered = false;
15974             
15975             this.render(this.parent().getChildContainer(true), k);
15976         }
15977         
15978         if(Roo.isIOS && this.useNativeIOS){
15979             this.initIOSView();
15980             return;
15981         }
15982         
15983         /*
15984          * Touch Devices
15985          */
15986         
15987         if(Roo.isTouch && this.mobileTouchView){
15988             this.initTouchView();
15989             return;
15990         }
15991         
15992         if(this.tickable){
15993             this.initTickableEvents();
15994             return;
15995         }
15996         
15997         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15998         
15999         if(this.hiddenName){
16000             
16001             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16002             
16003             this.hiddenField.dom.value =
16004                 this.hiddenValue !== undefined ? this.hiddenValue :
16005                 this.value !== undefined ? this.value : '';
16006
16007             // prevent input submission
16008             this.el.dom.removeAttribute('name');
16009             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16010              
16011              
16012         }
16013         //if(Roo.isGecko){
16014         //    this.el.dom.setAttribute('autocomplete', 'off');
16015         //}
16016         
16017         var cls = 'x-combo-list';
16018         
16019         //this.list = new Roo.Layer({
16020         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16021         //});
16022         
16023         var _this = this;
16024         
16025         (function(){
16026             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16027             _this.list.setWidth(lw);
16028         }).defer(100);
16029         
16030         this.list.on('mouseover', this.onViewOver, this);
16031         this.list.on('mousemove', this.onViewMove, this);
16032         this.list.on('scroll', this.onViewScroll, this);
16033         
16034         /*
16035         this.list.swallowEvent('mousewheel');
16036         this.assetHeight = 0;
16037
16038         if(this.title){
16039             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16040             this.assetHeight += this.header.getHeight();
16041         }
16042
16043         this.innerList = this.list.createChild({cls:cls+'-inner'});
16044         this.innerList.on('mouseover', this.onViewOver, this);
16045         this.innerList.on('mousemove', this.onViewMove, this);
16046         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16047         
16048         if(this.allowBlank && !this.pageSize && !this.disableClear){
16049             this.footer = this.list.createChild({cls:cls+'-ft'});
16050             this.pageTb = new Roo.Toolbar(this.footer);
16051            
16052         }
16053         if(this.pageSize){
16054             this.footer = this.list.createChild({cls:cls+'-ft'});
16055             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16056                     {pageSize: this.pageSize});
16057             
16058         }
16059         
16060         if (this.pageTb && this.allowBlank && !this.disableClear) {
16061             var _this = this;
16062             this.pageTb.add(new Roo.Toolbar.Fill(), {
16063                 cls: 'x-btn-icon x-btn-clear',
16064                 text: '&#160;',
16065                 handler: function()
16066                 {
16067                     _this.collapse();
16068                     _this.clearValue();
16069                     _this.onSelect(false, -1);
16070                 }
16071             });
16072         }
16073         if (this.footer) {
16074             this.assetHeight += this.footer.getHeight();
16075         }
16076         */
16077             
16078         if(!this.tpl){
16079             this.tpl = Roo.bootstrap.version == 4 ?
16080                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16081                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16082         }
16083
16084         this.view = new Roo.View(this.list, this.tpl, {
16085             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16086         });
16087         //this.view.wrapEl.setDisplayed(false);
16088         this.view.on('click', this.onViewClick, this);
16089         
16090         
16091         this.store.on('beforeload', this.onBeforeLoad, this);
16092         this.store.on('load', this.onLoad, this);
16093         this.store.on('loadexception', this.onLoadException, this);
16094         /*
16095         if(this.resizable){
16096             this.resizer = new Roo.Resizable(this.list,  {
16097                pinned:true, handles:'se'
16098             });
16099             this.resizer.on('resize', function(r, w, h){
16100                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16101                 this.listWidth = w;
16102                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16103                 this.restrictHeight();
16104             }, this);
16105             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16106         }
16107         */
16108         if(!this.editable){
16109             this.editable = true;
16110             this.setEditable(false);
16111         }
16112         
16113         /*
16114         
16115         if (typeof(this.events.add.listeners) != 'undefined') {
16116             
16117             this.addicon = this.wrap.createChild(
16118                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16119        
16120             this.addicon.on('click', function(e) {
16121                 this.fireEvent('add', this);
16122             }, this);
16123         }
16124         if (typeof(this.events.edit.listeners) != 'undefined') {
16125             
16126             this.editicon = this.wrap.createChild(
16127                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16128             if (this.addicon) {
16129                 this.editicon.setStyle('margin-left', '40px');
16130             }
16131             this.editicon.on('click', function(e) {
16132                 
16133                 // we fire even  if inothing is selected..
16134                 this.fireEvent('edit', this, this.lastData );
16135                 
16136             }, this);
16137         }
16138         */
16139         
16140         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16141             "up" : function(e){
16142                 this.inKeyMode = true;
16143                 this.selectPrev();
16144             },
16145
16146             "down" : function(e){
16147                 if(!this.isExpanded()){
16148                     this.onTriggerClick();
16149                 }else{
16150                     this.inKeyMode = true;
16151                     this.selectNext();
16152                 }
16153             },
16154
16155             "enter" : function(e){
16156 //                this.onViewClick();
16157                 //return true;
16158                 this.collapse();
16159                 
16160                 if(this.fireEvent("specialkey", this, e)){
16161                     this.onViewClick(false);
16162                 }
16163                 
16164                 return true;
16165             },
16166
16167             "esc" : function(e){
16168                 this.collapse();
16169             },
16170
16171             "tab" : function(e){
16172                 this.collapse();
16173                 
16174                 if(this.fireEvent("specialkey", this, e)){
16175                     this.onViewClick(false);
16176                 }
16177                 
16178                 return true;
16179             },
16180
16181             scope : this,
16182
16183             doRelay : function(foo, bar, hname){
16184                 if(hname == 'down' || this.scope.isExpanded()){
16185                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16186                 }
16187                 return true;
16188             },
16189
16190             forceKeyDown: true
16191         });
16192         
16193         
16194         this.queryDelay = Math.max(this.queryDelay || 10,
16195                 this.mode == 'local' ? 10 : 250);
16196         
16197         
16198         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16199         
16200         if(this.typeAhead){
16201             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16202         }
16203         if(this.editable !== false){
16204             this.inputEl().on("keyup", this.onKeyUp, this);
16205         }
16206         if(this.forceSelection){
16207             this.inputEl().on('blur', this.doForce, this);
16208         }
16209         
16210         if(this.multiple){
16211             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16212             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16213         }
16214     },
16215     
16216     initTickableEvents: function()
16217     {   
16218         this.createList();
16219         
16220         if(this.hiddenName){
16221             
16222             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16223             
16224             this.hiddenField.dom.value =
16225                 this.hiddenValue !== undefined ? this.hiddenValue :
16226                 this.value !== undefined ? this.value : '';
16227
16228             // prevent input submission
16229             this.el.dom.removeAttribute('name');
16230             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16231              
16232              
16233         }
16234         
16235 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16236         
16237         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16238         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16239         if(this.triggerList){
16240             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16241         }
16242          
16243         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16244         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16245         
16246         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16247         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16248         
16249         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16250         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16251         
16252         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16253         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16254         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16255         
16256         this.okBtn.hide();
16257         this.cancelBtn.hide();
16258         
16259         var _this = this;
16260         
16261         (function(){
16262             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16263             _this.list.setWidth(lw);
16264         }).defer(100);
16265         
16266         this.list.on('mouseover', this.onViewOver, this);
16267         this.list.on('mousemove', this.onViewMove, this);
16268         
16269         this.list.on('scroll', this.onViewScroll, this);
16270         
16271         if(!this.tpl){
16272             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16273                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16274         }
16275
16276         this.view = new Roo.View(this.list, this.tpl, {
16277             singleSelect:true,
16278             tickable:true,
16279             parent:this,
16280             store: this.store,
16281             selectedClass: this.selectedClass
16282         });
16283         
16284         //this.view.wrapEl.setDisplayed(false);
16285         this.view.on('click', this.onViewClick, this);
16286         
16287         
16288         
16289         this.store.on('beforeload', this.onBeforeLoad, this);
16290         this.store.on('load', this.onLoad, this);
16291         this.store.on('loadexception', this.onLoadException, this);
16292         
16293         if(this.editable){
16294             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16295                 "up" : function(e){
16296                     this.inKeyMode = true;
16297                     this.selectPrev();
16298                 },
16299
16300                 "down" : function(e){
16301                     this.inKeyMode = true;
16302                     this.selectNext();
16303                 },
16304
16305                 "enter" : function(e){
16306                     if(this.fireEvent("specialkey", this, e)){
16307                         this.onViewClick(false);
16308                     }
16309                     
16310                     return true;
16311                 },
16312
16313                 "esc" : function(e){
16314                     this.onTickableFooterButtonClick(e, false, false);
16315                 },
16316
16317                 "tab" : function(e){
16318                     this.fireEvent("specialkey", this, e);
16319                     
16320                     this.onTickableFooterButtonClick(e, false, false);
16321                     
16322                     return true;
16323                 },
16324
16325                 scope : this,
16326
16327                 doRelay : function(e, fn, key){
16328                     if(this.scope.isExpanded()){
16329                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16330                     }
16331                     return true;
16332                 },
16333
16334                 forceKeyDown: true
16335             });
16336         }
16337         
16338         this.queryDelay = Math.max(this.queryDelay || 10,
16339                 this.mode == 'local' ? 10 : 250);
16340         
16341         
16342         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16343         
16344         if(this.typeAhead){
16345             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16346         }
16347         
16348         if(this.editable !== false){
16349             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16350         }
16351         
16352         this.indicator = this.indicatorEl();
16353         
16354         if(this.indicator){
16355             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16356             this.indicator.hide();
16357         }
16358         
16359     },
16360
16361     onDestroy : function(){
16362         if(this.view){
16363             this.view.setStore(null);
16364             this.view.el.removeAllListeners();
16365             this.view.el.remove();
16366             this.view.purgeListeners();
16367         }
16368         if(this.list){
16369             this.list.dom.innerHTML  = '';
16370         }
16371         
16372         if(this.store){
16373             this.store.un('beforeload', this.onBeforeLoad, this);
16374             this.store.un('load', this.onLoad, this);
16375             this.store.un('loadexception', this.onLoadException, this);
16376         }
16377         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16378     },
16379
16380     // private
16381     fireKey : function(e){
16382         if(e.isNavKeyPress() && !this.list.isVisible()){
16383             this.fireEvent("specialkey", this, e);
16384         }
16385     },
16386
16387     // private
16388     onResize: function(w, h)
16389     {
16390         
16391         
16392 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16393 //        
16394 //        if(typeof w != 'number'){
16395 //            // we do not handle it!?!?
16396 //            return;
16397 //        }
16398 //        var tw = this.trigger.getWidth();
16399 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16400 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16401 //        var x = w - tw;
16402 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16403 //            
16404 //        //this.trigger.setStyle('left', x+'px');
16405 //        
16406 //        if(this.list && this.listWidth === undefined){
16407 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16408 //            this.list.setWidth(lw);
16409 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16410 //        }
16411         
16412     
16413         
16414     },
16415
16416     /**
16417      * Allow or prevent the user from directly editing the field text.  If false is passed,
16418      * the user will only be able to select from the items defined in the dropdown list.  This method
16419      * is the runtime equivalent of setting the 'editable' config option at config time.
16420      * @param {Boolean} value True to allow the user to directly edit the field text
16421      */
16422     setEditable : function(value){
16423         if(value == this.editable){
16424             return;
16425         }
16426         this.editable = value;
16427         if(!value){
16428             this.inputEl().dom.setAttribute('readOnly', true);
16429             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16430             this.inputEl().addClass('x-combo-noedit');
16431         }else{
16432             this.inputEl().dom.removeAttribute('readOnly');
16433             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16434             this.inputEl().removeClass('x-combo-noedit');
16435         }
16436     },
16437
16438     // private
16439     
16440     onBeforeLoad : function(combo,opts){
16441         if(!this.hasFocus){
16442             return;
16443         }
16444          if (!opts.add) {
16445             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16446          }
16447         this.restrictHeight();
16448         this.selectedIndex = -1;
16449     },
16450
16451     // private
16452     onLoad : function(){
16453         
16454         this.hasQuery = false;
16455         
16456         if(!this.hasFocus){
16457             return;
16458         }
16459         
16460         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16461             this.loading.hide();
16462         }
16463         
16464         if(this.store.getCount() > 0){
16465             
16466             this.expand();
16467             this.restrictHeight();
16468             if(this.lastQuery == this.allQuery){
16469                 if(this.editable && !this.tickable){
16470                     this.inputEl().dom.select();
16471                 }
16472                 
16473                 if(
16474                     !this.selectByValue(this.value, true) &&
16475                     this.autoFocus && 
16476                     (
16477                         !this.store.lastOptions ||
16478                         typeof(this.store.lastOptions.add) == 'undefined' || 
16479                         this.store.lastOptions.add != true
16480                     )
16481                 ){
16482                     this.select(0, true);
16483                 }
16484             }else{
16485                 if(this.autoFocus){
16486                     this.selectNext();
16487                 }
16488                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16489                     this.taTask.delay(this.typeAheadDelay);
16490                 }
16491             }
16492         }else{
16493             this.onEmptyResults();
16494         }
16495         
16496         //this.el.focus();
16497     },
16498     // private
16499     onLoadException : function()
16500     {
16501         this.hasQuery = false;
16502         
16503         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16504             this.loading.hide();
16505         }
16506         
16507         if(this.tickable && this.editable){
16508             return;
16509         }
16510         
16511         this.collapse();
16512         // only causes errors at present
16513         //Roo.log(this.store.reader.jsonData);
16514         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16515             // fixme
16516             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16517         //}
16518         
16519         
16520     },
16521     // private
16522     onTypeAhead : function(){
16523         if(this.store.getCount() > 0){
16524             var r = this.store.getAt(0);
16525             var newValue = r.data[this.displayField];
16526             var len = newValue.length;
16527             var selStart = this.getRawValue().length;
16528             
16529             if(selStart != len){
16530                 this.setRawValue(newValue);
16531                 this.selectText(selStart, newValue.length);
16532             }
16533         }
16534     },
16535
16536     // private
16537     onSelect : function(record, index){
16538         
16539         if(this.fireEvent('beforeselect', this, record, index) !== false){
16540         
16541             this.setFromData(index > -1 ? record.data : false);
16542             
16543             this.collapse();
16544             this.fireEvent('select', this, record, index);
16545         }
16546     },
16547
16548     /**
16549      * Returns the currently selected field value or empty string if no value is set.
16550      * @return {String} value The selected value
16551      */
16552     getValue : function()
16553     {
16554         if(Roo.isIOS && this.useNativeIOS){
16555             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16556         }
16557         
16558         if(this.multiple){
16559             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16560         }
16561         
16562         if(this.valueField){
16563             return typeof this.value != 'undefined' ? this.value : '';
16564         }else{
16565             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16566         }
16567     },
16568     
16569     getRawValue : function()
16570     {
16571         if(Roo.isIOS && this.useNativeIOS){
16572             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16573         }
16574         
16575         var v = this.inputEl().getValue();
16576         
16577         return v;
16578     },
16579
16580     /**
16581      * Clears any text/value currently set in the field
16582      */
16583     clearValue : function(){
16584         
16585         if(this.hiddenField){
16586             this.hiddenField.dom.value = '';
16587         }
16588         this.value = '';
16589         this.setRawValue('');
16590         this.lastSelectionText = '';
16591         this.lastData = false;
16592         
16593         var close = this.closeTriggerEl();
16594         
16595         if(close){
16596             close.hide();
16597         }
16598         
16599         this.validate();
16600         
16601     },
16602
16603     /**
16604      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16605      * will be displayed in the field.  If the value does not match the data value of an existing item,
16606      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16607      * Otherwise the field will be blank (although the value will still be set).
16608      * @param {String} value The value to match
16609      */
16610     setValue : function(v)
16611     {
16612         if(Roo.isIOS && this.useNativeIOS){
16613             this.setIOSValue(v);
16614             return;
16615         }
16616         
16617         if(this.multiple){
16618             this.syncValue();
16619             return;
16620         }
16621         
16622         var text = v;
16623         if(this.valueField){
16624             var r = this.findRecord(this.valueField, v);
16625             if(r){
16626                 text = r.data[this.displayField];
16627             }else if(this.valueNotFoundText !== undefined){
16628                 text = this.valueNotFoundText;
16629             }
16630         }
16631         this.lastSelectionText = text;
16632         if(this.hiddenField){
16633             this.hiddenField.dom.value = v;
16634         }
16635         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16636         this.value = v;
16637         
16638         var close = this.closeTriggerEl();
16639         
16640         if(close){
16641             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16642         }
16643         
16644         this.validate();
16645     },
16646     /**
16647      * @property {Object} the last set data for the element
16648      */
16649     
16650     lastData : false,
16651     /**
16652      * Sets the value of the field based on a object which is related to the record format for the store.
16653      * @param {Object} value the value to set as. or false on reset?
16654      */
16655     setFromData : function(o){
16656         
16657         if(this.multiple){
16658             this.addItem(o);
16659             return;
16660         }
16661             
16662         var dv = ''; // display value
16663         var vv = ''; // value value..
16664         this.lastData = o;
16665         if (this.displayField) {
16666             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16667         } else {
16668             // this is an error condition!!!
16669             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16670         }
16671         
16672         if(this.valueField){
16673             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16674         }
16675         
16676         var close = this.closeTriggerEl();
16677         
16678         if(close){
16679             if(dv.length || vv * 1 > 0){
16680                 close.show() ;
16681                 this.blockFocus=true;
16682             } else {
16683                 close.hide();
16684             }             
16685         }
16686         
16687         if(this.hiddenField){
16688             this.hiddenField.dom.value = vv;
16689             
16690             this.lastSelectionText = dv;
16691             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16692             this.value = vv;
16693             return;
16694         }
16695         // no hidden field.. - we store the value in 'value', but still display
16696         // display field!!!!
16697         this.lastSelectionText = dv;
16698         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16699         this.value = vv;
16700         
16701         
16702         
16703     },
16704     // private
16705     reset : function(){
16706         // overridden so that last data is reset..
16707         
16708         if(this.multiple){
16709             this.clearItem();
16710             return;
16711         }
16712         
16713         this.setValue(this.originalValue);
16714         //this.clearInvalid();
16715         this.lastData = false;
16716         if (this.view) {
16717             this.view.clearSelections();
16718         }
16719         
16720         this.validate();
16721     },
16722     // private
16723     findRecord : function(prop, value){
16724         var record;
16725         if(this.store.getCount() > 0){
16726             this.store.each(function(r){
16727                 if(r.data[prop] == value){
16728                     record = r;
16729                     return false;
16730                 }
16731                 return true;
16732             });
16733         }
16734         return record;
16735     },
16736     
16737     getName: function()
16738     {
16739         // returns hidden if it's set..
16740         if (!this.rendered) {return ''};
16741         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16742         
16743     },
16744     // private
16745     onViewMove : function(e, t){
16746         this.inKeyMode = false;
16747     },
16748
16749     // private
16750     onViewOver : function(e, t){
16751         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16752             return;
16753         }
16754         var item = this.view.findItemFromChild(t);
16755         
16756         if(item){
16757             var index = this.view.indexOf(item);
16758             this.select(index, false);
16759         }
16760     },
16761
16762     // private
16763     onViewClick : function(view, doFocus, el, e)
16764     {
16765         var index = this.view.getSelectedIndexes()[0];
16766         
16767         var r = this.store.getAt(index);
16768         
16769         if(this.tickable){
16770             
16771             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16772                 return;
16773             }
16774             
16775             var rm = false;
16776             var _this = this;
16777             
16778             Roo.each(this.tickItems, function(v,k){
16779                 
16780                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16781                     Roo.log(v);
16782                     _this.tickItems.splice(k, 1);
16783                     
16784                     if(typeof(e) == 'undefined' && view == false){
16785                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16786                     }
16787                     
16788                     rm = true;
16789                     return;
16790                 }
16791             });
16792             
16793             if(rm){
16794                 return;
16795             }
16796             
16797             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16798                 this.tickItems.push(r.data);
16799             }
16800             
16801             if(typeof(e) == 'undefined' && view == false){
16802                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16803             }
16804                     
16805             return;
16806         }
16807         
16808         if(r){
16809             this.onSelect(r, index);
16810         }
16811         if(doFocus !== false && !this.blockFocus){
16812             this.inputEl().focus();
16813         }
16814     },
16815
16816     // private
16817     restrictHeight : function(){
16818         //this.innerList.dom.style.height = '';
16819         //var inner = this.innerList.dom;
16820         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16821         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16822         //this.list.beginUpdate();
16823         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16824         this.list.alignTo(this.inputEl(), this.listAlign);
16825         this.list.alignTo(this.inputEl(), this.listAlign);
16826         //this.list.endUpdate();
16827     },
16828
16829     // private
16830     onEmptyResults : function(){
16831         
16832         if(this.tickable && this.editable){
16833             this.hasFocus = false;
16834             this.restrictHeight();
16835             return;
16836         }
16837         
16838         this.collapse();
16839     },
16840
16841     /**
16842      * Returns true if the dropdown list is expanded, else false.
16843      */
16844     isExpanded : function(){
16845         return this.list.isVisible();
16846     },
16847
16848     /**
16849      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16850      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16851      * @param {String} value The data value of the item to select
16852      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16853      * selected item if it is not currently in view (defaults to true)
16854      * @return {Boolean} True if the value matched an item in the list, else false
16855      */
16856     selectByValue : function(v, scrollIntoView){
16857         if(v !== undefined && v !== null){
16858             var r = this.findRecord(this.valueField || this.displayField, v);
16859             if(r){
16860                 this.select(this.store.indexOf(r), scrollIntoView);
16861                 return true;
16862             }
16863         }
16864         return false;
16865     },
16866
16867     /**
16868      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16869      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16870      * @param {Number} index The zero-based index of the list item to select
16871      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16872      * selected item if it is not currently in view (defaults to true)
16873      */
16874     select : function(index, scrollIntoView){
16875         this.selectedIndex = index;
16876         this.view.select(index);
16877         if(scrollIntoView !== false){
16878             var el = this.view.getNode(index);
16879             /*
16880              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16881              */
16882             if(el){
16883                 this.list.scrollChildIntoView(el, false);
16884             }
16885         }
16886     },
16887
16888     // private
16889     selectNext : function(){
16890         var ct = this.store.getCount();
16891         if(ct > 0){
16892             if(this.selectedIndex == -1){
16893                 this.select(0);
16894             }else if(this.selectedIndex < ct-1){
16895                 this.select(this.selectedIndex+1);
16896             }
16897         }
16898     },
16899
16900     // private
16901     selectPrev : function(){
16902         var ct = this.store.getCount();
16903         if(ct > 0){
16904             if(this.selectedIndex == -1){
16905                 this.select(0);
16906             }else if(this.selectedIndex != 0){
16907                 this.select(this.selectedIndex-1);
16908             }
16909         }
16910     },
16911
16912     // private
16913     onKeyUp : function(e){
16914         if(this.editable !== false && !e.isSpecialKey()){
16915             this.lastKey = e.getKey();
16916             this.dqTask.delay(this.queryDelay);
16917         }
16918     },
16919
16920     // private
16921     validateBlur : function(){
16922         return !this.list || !this.list.isVisible();   
16923     },
16924
16925     // private
16926     initQuery : function(){
16927         
16928         var v = this.getRawValue();
16929         
16930         if(this.tickable && this.editable){
16931             v = this.tickableInputEl().getValue();
16932         }
16933         
16934         this.doQuery(v);
16935     },
16936
16937     // private
16938     doForce : function(){
16939         if(this.inputEl().dom.value.length > 0){
16940             this.inputEl().dom.value =
16941                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16942              
16943         }
16944     },
16945
16946     /**
16947      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16948      * query allowing the query action to be canceled if needed.
16949      * @param {String} query The SQL query to execute
16950      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16951      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16952      * saved in the current store (defaults to false)
16953      */
16954     doQuery : function(q, forceAll){
16955         
16956         if(q === undefined || q === null){
16957             q = '';
16958         }
16959         var qe = {
16960             query: q,
16961             forceAll: forceAll,
16962             combo: this,
16963             cancel:false
16964         };
16965         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16966             return false;
16967         }
16968         q = qe.query;
16969         
16970         forceAll = qe.forceAll;
16971         if(forceAll === true || (q.length >= this.minChars)){
16972             
16973             this.hasQuery = true;
16974             
16975             if(this.lastQuery != q || this.alwaysQuery){
16976                 this.lastQuery = q;
16977                 if(this.mode == 'local'){
16978                     this.selectedIndex = -1;
16979                     if(forceAll){
16980                         this.store.clearFilter();
16981                     }else{
16982                         
16983                         if(this.specialFilter){
16984                             this.fireEvent('specialfilter', this);
16985                             this.onLoad();
16986                             return;
16987                         }
16988                         
16989                         this.store.filter(this.displayField, q);
16990                     }
16991                     
16992                     this.store.fireEvent("datachanged", this.store);
16993                     
16994                     this.onLoad();
16995                     
16996                     
16997                 }else{
16998                     
16999                     this.store.baseParams[this.queryParam] = q;
17000                     
17001                     var options = {params : this.getParams(q)};
17002                     
17003                     if(this.loadNext){
17004                         options.add = true;
17005                         options.params.start = this.page * this.pageSize;
17006                     }
17007                     
17008                     this.store.load(options);
17009                     
17010                     /*
17011                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17012                      *  we should expand the list on onLoad
17013                      *  so command out it
17014                      */
17015 //                    this.expand();
17016                 }
17017             }else{
17018                 this.selectedIndex = -1;
17019                 this.onLoad();   
17020             }
17021         }
17022         
17023         this.loadNext = false;
17024     },
17025     
17026     // private
17027     getParams : function(q){
17028         var p = {};
17029         //p[this.queryParam] = q;
17030         
17031         if(this.pageSize){
17032             p.start = 0;
17033             p.limit = this.pageSize;
17034         }
17035         return p;
17036     },
17037
17038     /**
17039      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17040      */
17041     collapse : function(){
17042         if(!this.isExpanded()){
17043             return;
17044         }
17045         
17046         this.list.hide();
17047         
17048         this.hasFocus = false;
17049         
17050         if(this.tickable){
17051             this.okBtn.hide();
17052             this.cancelBtn.hide();
17053             this.trigger.show();
17054             
17055             if(this.editable){
17056                 this.tickableInputEl().dom.value = '';
17057                 this.tickableInputEl().blur();
17058             }
17059             
17060         }
17061         
17062         Roo.get(document).un('mousedown', this.collapseIf, this);
17063         Roo.get(document).un('mousewheel', this.collapseIf, this);
17064         if (!this.editable) {
17065             Roo.get(document).un('keydown', this.listKeyPress, this);
17066         }
17067         this.fireEvent('collapse', this);
17068         
17069         this.validate();
17070     },
17071
17072     // private
17073     collapseIf : function(e){
17074         var in_combo  = e.within(this.el);
17075         var in_list =  e.within(this.list);
17076         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17077         
17078         if (in_combo || in_list || is_list) {
17079             //e.stopPropagation();
17080             return;
17081         }
17082         
17083         if(this.tickable){
17084             this.onTickableFooterButtonClick(e, false, false);
17085         }
17086
17087         this.collapse();
17088         
17089     },
17090
17091     /**
17092      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17093      */
17094     expand : function(){
17095        
17096         if(this.isExpanded() || !this.hasFocus){
17097             return;
17098         }
17099         
17100         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17101         this.list.setWidth(lw);
17102         
17103         Roo.log('expand');
17104         
17105         this.list.show();
17106         
17107         this.restrictHeight();
17108         
17109         if(this.tickable){
17110             
17111             this.tickItems = Roo.apply([], this.item);
17112             
17113             this.okBtn.show();
17114             this.cancelBtn.show();
17115             this.trigger.hide();
17116             
17117             if(this.editable){
17118                 this.tickableInputEl().focus();
17119             }
17120             
17121         }
17122         
17123         Roo.get(document).on('mousedown', this.collapseIf, this);
17124         Roo.get(document).on('mousewheel', this.collapseIf, this);
17125         if (!this.editable) {
17126             Roo.get(document).on('keydown', this.listKeyPress, this);
17127         }
17128         
17129         this.fireEvent('expand', this);
17130     },
17131
17132     // private
17133     // Implements the default empty TriggerField.onTriggerClick function
17134     onTriggerClick : function(e)
17135     {
17136         Roo.log('trigger click');
17137         
17138         if(this.disabled || !this.triggerList){
17139             return;
17140         }
17141         
17142         this.page = 0;
17143         this.loadNext = false;
17144         
17145         if(this.isExpanded()){
17146             this.collapse();
17147             if (!this.blockFocus) {
17148                 this.inputEl().focus();
17149             }
17150             
17151         }else {
17152             this.hasFocus = true;
17153             if(this.triggerAction == 'all') {
17154                 this.doQuery(this.allQuery, true);
17155             } else {
17156                 this.doQuery(this.getRawValue());
17157             }
17158             if (!this.blockFocus) {
17159                 this.inputEl().focus();
17160             }
17161         }
17162     },
17163     
17164     onTickableTriggerClick : function(e)
17165     {
17166         if(this.disabled){
17167             return;
17168         }
17169         
17170         this.page = 0;
17171         this.loadNext = false;
17172         this.hasFocus = true;
17173         
17174         if(this.triggerAction == 'all') {
17175             this.doQuery(this.allQuery, true);
17176         } else {
17177             this.doQuery(this.getRawValue());
17178         }
17179     },
17180     
17181     onSearchFieldClick : function(e)
17182     {
17183         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17184             this.onTickableFooterButtonClick(e, false, false);
17185             return;
17186         }
17187         
17188         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17189             return;
17190         }
17191         
17192         this.page = 0;
17193         this.loadNext = false;
17194         this.hasFocus = true;
17195         
17196         if(this.triggerAction == 'all') {
17197             this.doQuery(this.allQuery, true);
17198         } else {
17199             this.doQuery(this.getRawValue());
17200         }
17201     },
17202     
17203     listKeyPress : function(e)
17204     {
17205         //Roo.log('listkeypress');
17206         // scroll to first matching element based on key pres..
17207         if (e.isSpecialKey()) {
17208             return false;
17209         }
17210         var k = String.fromCharCode(e.getKey()).toUpperCase();
17211         //Roo.log(k);
17212         var match  = false;
17213         var csel = this.view.getSelectedNodes();
17214         var cselitem = false;
17215         if (csel.length) {
17216             var ix = this.view.indexOf(csel[0]);
17217             cselitem  = this.store.getAt(ix);
17218             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17219                 cselitem = false;
17220             }
17221             
17222         }
17223         
17224         this.store.each(function(v) { 
17225             if (cselitem) {
17226                 // start at existing selection.
17227                 if (cselitem.id == v.id) {
17228                     cselitem = false;
17229                 }
17230                 return true;
17231             }
17232                 
17233             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17234                 match = this.store.indexOf(v);
17235                 return false;
17236             }
17237             return true;
17238         }, this);
17239         
17240         if (match === false) {
17241             return true; // no more action?
17242         }
17243         // scroll to?
17244         this.view.select(match);
17245         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17246         sn.scrollIntoView(sn.dom.parentNode, false);
17247     },
17248     
17249     onViewScroll : function(e, t){
17250         
17251         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){
17252             return;
17253         }
17254         
17255         this.hasQuery = true;
17256         
17257         this.loading = this.list.select('.loading', true).first();
17258         
17259         if(this.loading === null){
17260             this.list.createChild({
17261                 tag: 'div',
17262                 cls: 'loading roo-select2-more-results roo-select2-active',
17263                 html: 'Loading more results...'
17264             });
17265             
17266             this.loading = this.list.select('.loading', true).first();
17267             
17268             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17269             
17270             this.loading.hide();
17271         }
17272         
17273         this.loading.show();
17274         
17275         var _combo = this;
17276         
17277         this.page++;
17278         this.loadNext = true;
17279         
17280         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17281         
17282         return;
17283     },
17284     
17285     addItem : function(o)
17286     {   
17287         var dv = ''; // display value
17288         
17289         if (this.displayField) {
17290             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17291         } else {
17292             // this is an error condition!!!
17293             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17294         }
17295         
17296         if(!dv.length){
17297             return;
17298         }
17299         
17300         var choice = this.choices.createChild({
17301             tag: 'li',
17302             cls: 'roo-select2-search-choice',
17303             cn: [
17304                 {
17305                     tag: 'div',
17306                     html: dv
17307                 },
17308                 {
17309                     tag: 'a',
17310                     href: '#',
17311                     cls: 'roo-select2-search-choice-close fa fa-times',
17312                     tabindex: '-1'
17313                 }
17314             ]
17315             
17316         }, this.searchField);
17317         
17318         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17319         
17320         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17321         
17322         this.item.push(o);
17323         
17324         this.lastData = o;
17325         
17326         this.syncValue();
17327         
17328         this.inputEl().dom.value = '';
17329         
17330         this.validate();
17331     },
17332     
17333     onRemoveItem : function(e, _self, o)
17334     {
17335         e.preventDefault();
17336         
17337         this.lastItem = Roo.apply([], this.item);
17338         
17339         var index = this.item.indexOf(o.data) * 1;
17340         
17341         if( index < 0){
17342             Roo.log('not this item?!');
17343             return;
17344         }
17345         
17346         this.item.splice(index, 1);
17347         o.item.remove();
17348         
17349         this.syncValue();
17350         
17351         this.fireEvent('remove', this, e);
17352         
17353         this.validate();
17354         
17355     },
17356     
17357     syncValue : function()
17358     {
17359         if(!this.item.length){
17360             this.clearValue();
17361             return;
17362         }
17363             
17364         var value = [];
17365         var _this = this;
17366         Roo.each(this.item, function(i){
17367             if(_this.valueField){
17368                 value.push(i[_this.valueField]);
17369                 return;
17370             }
17371
17372             value.push(i);
17373         });
17374
17375         this.value = value.join(',');
17376
17377         if(this.hiddenField){
17378             this.hiddenField.dom.value = this.value;
17379         }
17380         
17381         this.store.fireEvent("datachanged", this.store);
17382         
17383         this.validate();
17384     },
17385     
17386     clearItem : function()
17387     {
17388         if(!this.multiple){
17389             return;
17390         }
17391         
17392         this.item = [];
17393         
17394         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17395            c.remove();
17396         });
17397         
17398         this.syncValue();
17399         
17400         this.validate();
17401         
17402         if(this.tickable && !Roo.isTouch){
17403             this.view.refresh();
17404         }
17405     },
17406     
17407     inputEl: function ()
17408     {
17409         if(Roo.isIOS && this.useNativeIOS){
17410             return this.el.select('select.roo-ios-select', true).first();
17411         }
17412         
17413         if(Roo.isTouch && this.mobileTouchView){
17414             return this.el.select('input.form-control',true).first();
17415         }
17416         
17417         if(this.tickable){
17418             return this.searchField;
17419         }
17420         
17421         return this.el.select('input.form-control',true).first();
17422     },
17423     
17424     onTickableFooterButtonClick : function(e, btn, el)
17425     {
17426         e.preventDefault();
17427         
17428         this.lastItem = Roo.apply([], this.item);
17429         
17430         if(btn && btn.name == 'cancel'){
17431             this.tickItems = Roo.apply([], this.item);
17432             this.collapse();
17433             return;
17434         }
17435         
17436         this.clearItem();
17437         
17438         var _this = this;
17439         
17440         Roo.each(this.tickItems, function(o){
17441             _this.addItem(o);
17442         });
17443         
17444         this.collapse();
17445         
17446     },
17447     
17448     validate : function()
17449     {
17450         if(this.getVisibilityEl().hasClass('hidden')){
17451             return true;
17452         }
17453         
17454         var v = this.getRawValue();
17455         
17456         if(this.multiple){
17457             v = this.getValue();
17458         }
17459         
17460         if(this.disabled || this.allowBlank || v.length){
17461             this.markValid();
17462             return true;
17463         }
17464         
17465         this.markInvalid();
17466         return false;
17467     },
17468     
17469     tickableInputEl : function()
17470     {
17471         if(!this.tickable || !this.editable){
17472             return this.inputEl();
17473         }
17474         
17475         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17476     },
17477     
17478     
17479     getAutoCreateTouchView : function()
17480     {
17481         var id = Roo.id();
17482         
17483         var cfg = {
17484             cls: 'form-group' //input-group
17485         };
17486         
17487         var input =  {
17488             tag: 'input',
17489             id : id,
17490             type : this.inputType,
17491             cls : 'form-control x-combo-noedit',
17492             autocomplete: 'new-password',
17493             placeholder : this.placeholder || '',
17494             readonly : true
17495         };
17496         
17497         if (this.name) {
17498             input.name = this.name;
17499         }
17500         
17501         if (this.size) {
17502             input.cls += ' input-' + this.size;
17503         }
17504         
17505         if (this.disabled) {
17506             input.disabled = true;
17507         }
17508         
17509         var inputblock = {
17510             cls : 'roo-combobox-wrap',
17511             cn : [
17512                 input
17513             ]
17514         };
17515         
17516         if(this.before){
17517             inputblock.cls += ' input-group';
17518             
17519             inputblock.cn.unshift({
17520                 tag :'span',
17521                 cls : 'input-group-addon input-group-prepend input-group-text',
17522                 html : this.before
17523             });
17524         }
17525         
17526         if(this.removable && !this.multiple){
17527             inputblock.cls += ' roo-removable';
17528             
17529             inputblock.cn.push({
17530                 tag: 'button',
17531                 html : 'x',
17532                 cls : 'roo-combo-removable-btn close'
17533             });
17534         }
17535
17536         if(this.hasFeedback && !this.allowBlank){
17537             
17538             inputblock.cls += ' has-feedback';
17539             
17540             inputblock.cn.push({
17541                 tag: 'span',
17542                 cls: 'glyphicon form-control-feedback'
17543             });
17544             
17545         }
17546         
17547         if (this.after) {
17548             
17549             inputblock.cls += (this.before) ? '' : ' input-group';
17550             
17551             inputblock.cn.push({
17552                 tag :'span',
17553                 cls : 'input-group-addon input-group-append input-group-text',
17554                 html : this.after
17555             });
17556         }
17557
17558         
17559         var ibwrap = inputblock;
17560         
17561         if(this.multiple){
17562             ibwrap = {
17563                 tag: 'ul',
17564                 cls: 'roo-select2-choices',
17565                 cn:[
17566                     {
17567                         tag: 'li',
17568                         cls: 'roo-select2-search-field',
17569                         cn: [
17570
17571                             inputblock
17572                         ]
17573                     }
17574                 ]
17575             };
17576         
17577             
17578         }
17579         
17580         var combobox = {
17581             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17582             cn: [
17583                 {
17584                     tag: 'input',
17585                     type : 'hidden',
17586                     cls: 'form-hidden-field'
17587                 },
17588                 ibwrap
17589             ]
17590         };
17591         
17592         if(!this.multiple && this.showToggleBtn){
17593             
17594             var caret = {
17595                 cls: 'caret'
17596             };
17597             
17598             if (this.caret != false) {
17599                 caret = {
17600                      tag: 'i',
17601                      cls: 'fa fa-' + this.caret
17602                 };
17603                 
17604             }
17605             
17606             combobox.cn.push({
17607                 tag :'span',
17608                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17609                 cn : [
17610                     Roo.bootstrap.version == 3 ? caret : '',
17611                     {
17612                         tag: 'span',
17613                         cls: 'combobox-clear',
17614                         cn  : [
17615                             {
17616                                 tag : 'i',
17617                                 cls: 'icon-remove'
17618                             }
17619                         ]
17620                     }
17621                 ]
17622
17623             })
17624         }
17625         
17626         if(this.multiple){
17627             combobox.cls += ' roo-select2-container-multi';
17628         }
17629         
17630         var required =  this.allowBlank ?  {
17631                     tag : 'i',
17632                     style: 'display: none'
17633                 } : {
17634                    tag : 'i',
17635                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17636                    tooltip : 'This field is required'
17637                 };
17638         
17639         var align = this.labelAlign || this.parentLabelAlign();
17640         
17641         if (align ==='left' && this.fieldLabel.length) {
17642
17643             cfg.cn = [
17644                 required,
17645                 {
17646                     tag: 'label',
17647                     cls : 'control-label col-form-label',
17648                     html : this.fieldLabel
17649
17650                 },
17651                 {
17652                     cls : 'roo-combobox-wrap ', 
17653                     cn: [
17654                         combobox
17655                     ]
17656                 }
17657             ];
17658             
17659             var labelCfg = cfg.cn[1];
17660             var contentCfg = cfg.cn[2];
17661             
17662
17663             if(this.indicatorpos == 'right'){
17664                 cfg.cn = [
17665                     {
17666                         tag: 'label',
17667                         'for' :  id,
17668                         cls : 'control-label col-form-label',
17669                         cn : [
17670                             {
17671                                 tag : 'span',
17672                                 html : this.fieldLabel
17673                             },
17674                             required
17675                         ]
17676                     },
17677                     {
17678                         cls : "roo-combobox-wrap ",
17679                         cn: [
17680                             combobox
17681                         ]
17682                     }
17683
17684                 ];
17685                 
17686                 labelCfg = cfg.cn[0];
17687                 contentCfg = cfg.cn[1];
17688             }
17689             
17690            
17691             
17692             if(this.labelWidth > 12){
17693                 labelCfg.style = "width: " + this.labelWidth + 'px';
17694             }
17695            
17696             if(this.labelWidth < 13 && this.labelmd == 0){
17697                 this.labelmd = this.labelWidth;
17698             }
17699             
17700             if(this.labellg > 0){
17701                 labelCfg.cls += ' col-lg-' + this.labellg;
17702                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17703             }
17704             
17705             if(this.labelmd > 0){
17706                 labelCfg.cls += ' col-md-' + this.labelmd;
17707                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17708             }
17709             
17710             if(this.labelsm > 0){
17711                 labelCfg.cls += ' col-sm-' + this.labelsm;
17712                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17713             }
17714             
17715             if(this.labelxs > 0){
17716                 labelCfg.cls += ' col-xs-' + this.labelxs;
17717                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17718             }
17719                 
17720                 
17721         } else if ( this.fieldLabel.length) {
17722             cfg.cn = [
17723                required,
17724                 {
17725                     tag: 'label',
17726                     cls : 'control-label',
17727                     html : this.fieldLabel
17728
17729                 },
17730                 {
17731                     cls : '', 
17732                     cn: [
17733                         combobox
17734                     ]
17735                 }
17736             ];
17737             
17738             if(this.indicatorpos == 'right'){
17739                 cfg.cn = [
17740                     {
17741                         tag: 'label',
17742                         cls : 'control-label',
17743                         html : this.fieldLabel,
17744                         cn : [
17745                             required
17746                         ]
17747                     },
17748                     {
17749                         cls : '', 
17750                         cn: [
17751                             combobox
17752                         ]
17753                     }
17754                 ];
17755             }
17756         } else {
17757             cfg.cn = combobox;    
17758         }
17759         
17760         
17761         var settings = this;
17762         
17763         ['xs','sm','md','lg'].map(function(size){
17764             if (settings[size]) {
17765                 cfg.cls += ' col-' + size + '-' + settings[size];
17766             }
17767         });
17768         
17769         return cfg;
17770     },
17771     
17772     initTouchView : function()
17773     {
17774         this.renderTouchView();
17775         
17776         this.touchViewEl.on('scroll', function(){
17777             this.el.dom.scrollTop = 0;
17778         }, this);
17779         
17780         this.originalValue = this.getValue();
17781         
17782         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17783         
17784         this.inputEl().on("click", this.showTouchView, this);
17785         if (this.triggerEl) {
17786             this.triggerEl.on("click", this.showTouchView, this);
17787         }
17788         
17789         
17790         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17791         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17792         
17793         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17794         
17795         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17796         this.store.on('load', this.onTouchViewLoad, this);
17797         this.store.on('loadexception', this.onTouchViewLoadException, this);
17798         
17799         if(this.hiddenName){
17800             
17801             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17802             
17803             this.hiddenField.dom.value =
17804                 this.hiddenValue !== undefined ? this.hiddenValue :
17805                 this.value !== undefined ? this.value : '';
17806         
17807             this.el.dom.removeAttribute('name');
17808             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17809         }
17810         
17811         if(this.multiple){
17812             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17813             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17814         }
17815         
17816         if(this.removable && !this.multiple){
17817             var close = this.closeTriggerEl();
17818             if(close){
17819                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17820                 close.on('click', this.removeBtnClick, this, close);
17821             }
17822         }
17823         /*
17824          * fix the bug in Safari iOS8
17825          */
17826         this.inputEl().on("focus", function(e){
17827             document.activeElement.blur();
17828         }, this);
17829         
17830         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17831         
17832         return;
17833         
17834         
17835     },
17836     
17837     renderTouchView : function()
17838     {
17839         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17840         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17841         
17842         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17843         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17844         
17845         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17846         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17847         this.touchViewBodyEl.setStyle('overflow', 'auto');
17848         
17849         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17850         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17851         
17852         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17853         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17854         
17855     },
17856     
17857     showTouchView : function()
17858     {
17859         if(this.disabled){
17860             return;
17861         }
17862         
17863         this.touchViewHeaderEl.hide();
17864
17865         if(this.modalTitle.length){
17866             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17867             this.touchViewHeaderEl.show();
17868         }
17869
17870         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17871         this.touchViewEl.show();
17872
17873         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17874         
17875         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17876         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17877
17878         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17879
17880         if(this.modalTitle.length){
17881             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17882         }
17883         
17884         this.touchViewBodyEl.setHeight(bodyHeight);
17885
17886         if(this.animate){
17887             var _this = this;
17888             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17889         }else{
17890             this.touchViewEl.addClass(['in','show']);
17891         }
17892         
17893         if(this._touchViewMask){
17894             Roo.get(document.body).addClass("x-body-masked");
17895             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17896             this._touchViewMask.setStyle('z-index', 10000);
17897             this._touchViewMask.addClass('show');
17898         }
17899         
17900         this.doTouchViewQuery();
17901         
17902     },
17903     
17904     hideTouchView : function()
17905     {
17906         this.touchViewEl.removeClass(['in','show']);
17907
17908         if(this.animate){
17909             var _this = this;
17910             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17911         }else{
17912             this.touchViewEl.setStyle('display', 'none');
17913         }
17914         
17915         if(this._touchViewMask){
17916             this._touchViewMask.removeClass('show');
17917             Roo.get(document.body).removeClass("x-body-masked");
17918         }
17919     },
17920     
17921     setTouchViewValue : function()
17922     {
17923         if(this.multiple){
17924             this.clearItem();
17925         
17926             var _this = this;
17927
17928             Roo.each(this.tickItems, function(o){
17929                 this.addItem(o);
17930             }, this);
17931         }
17932         
17933         this.hideTouchView();
17934     },
17935     
17936     doTouchViewQuery : function()
17937     {
17938         var qe = {
17939             query: '',
17940             forceAll: true,
17941             combo: this,
17942             cancel:false
17943         };
17944         
17945         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17946             return false;
17947         }
17948         
17949         if(!this.alwaysQuery || this.mode == 'local'){
17950             this.onTouchViewLoad();
17951             return;
17952         }
17953         
17954         this.store.load();
17955     },
17956     
17957     onTouchViewBeforeLoad : function(combo,opts)
17958     {
17959         return;
17960     },
17961
17962     // private
17963     onTouchViewLoad : function()
17964     {
17965         if(this.store.getCount() < 1){
17966             this.onTouchViewEmptyResults();
17967             return;
17968         }
17969         
17970         this.clearTouchView();
17971         
17972         var rawValue = this.getRawValue();
17973         
17974         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17975         
17976         this.tickItems = [];
17977         
17978         this.store.data.each(function(d, rowIndex){
17979             var row = this.touchViewListGroup.createChild(template);
17980             
17981             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17982                 row.addClass(d.data.cls);
17983             }
17984             
17985             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17986                 var cfg = {
17987                     data : d.data,
17988                     html : d.data[this.displayField]
17989                 };
17990                 
17991                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17992                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17993                 }
17994             }
17995             row.removeClass('selected');
17996             if(!this.multiple && this.valueField &&
17997                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17998             {
17999                 // radio buttons..
18000                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18001                 row.addClass('selected');
18002             }
18003             
18004             if(this.multiple && this.valueField &&
18005                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18006             {
18007                 
18008                 // checkboxes...
18009                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18010                 this.tickItems.push(d.data);
18011             }
18012             
18013             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18014             
18015         }, this);
18016         
18017         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18018         
18019         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18020
18021         if(this.modalTitle.length){
18022             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18023         }
18024
18025         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18026         
18027         if(this.mobile_restrict_height && listHeight < bodyHeight){
18028             this.touchViewBodyEl.setHeight(listHeight);
18029         }
18030         
18031         var _this = this;
18032         
18033         if(firstChecked && listHeight > bodyHeight){
18034             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18035         }
18036         
18037     },
18038     
18039     onTouchViewLoadException : function()
18040     {
18041         this.hideTouchView();
18042     },
18043     
18044     onTouchViewEmptyResults : function()
18045     {
18046         this.clearTouchView();
18047         
18048         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18049         
18050         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18051         
18052     },
18053     
18054     clearTouchView : function()
18055     {
18056         this.touchViewListGroup.dom.innerHTML = '';
18057     },
18058     
18059     onTouchViewClick : function(e, el, o)
18060     {
18061         e.preventDefault();
18062         
18063         var row = o.row;
18064         var rowIndex = o.rowIndex;
18065         
18066         var r = this.store.getAt(rowIndex);
18067         
18068         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18069             
18070             if(!this.multiple){
18071                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18072                     c.dom.removeAttribute('checked');
18073                 }, this);
18074
18075                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18076
18077                 this.setFromData(r.data);
18078
18079                 var close = this.closeTriggerEl();
18080
18081                 if(close){
18082                     close.show();
18083                 }
18084
18085                 this.hideTouchView();
18086
18087                 this.fireEvent('select', this, r, rowIndex);
18088
18089                 return;
18090             }
18091
18092             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18093                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18094                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18095                 return;
18096             }
18097
18098             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18099             this.addItem(r.data);
18100             this.tickItems.push(r.data);
18101         }
18102     },
18103     
18104     getAutoCreateNativeIOS : function()
18105     {
18106         var cfg = {
18107             cls: 'form-group' //input-group,
18108         };
18109         
18110         var combobox =  {
18111             tag: 'select',
18112             cls : 'roo-ios-select'
18113         };
18114         
18115         if (this.name) {
18116             combobox.name = this.name;
18117         }
18118         
18119         if (this.disabled) {
18120             combobox.disabled = true;
18121         }
18122         
18123         var settings = this;
18124         
18125         ['xs','sm','md','lg'].map(function(size){
18126             if (settings[size]) {
18127                 cfg.cls += ' col-' + size + '-' + settings[size];
18128             }
18129         });
18130         
18131         cfg.cn = combobox;
18132         
18133         return cfg;
18134         
18135     },
18136     
18137     initIOSView : function()
18138     {
18139         this.store.on('load', this.onIOSViewLoad, this);
18140         
18141         return;
18142     },
18143     
18144     onIOSViewLoad : function()
18145     {
18146         if(this.store.getCount() < 1){
18147             return;
18148         }
18149         
18150         this.clearIOSView();
18151         
18152         if(this.allowBlank) {
18153             
18154             var default_text = '-- SELECT --';
18155             
18156             if(this.placeholder.length){
18157                 default_text = this.placeholder;
18158             }
18159             
18160             if(this.emptyTitle.length){
18161                 default_text += ' - ' + this.emptyTitle + ' -';
18162             }
18163             
18164             var opt = this.inputEl().createChild({
18165                 tag: 'option',
18166                 value : 0,
18167                 html : default_text
18168             });
18169             
18170             var o = {};
18171             o[this.valueField] = 0;
18172             o[this.displayField] = default_text;
18173             
18174             this.ios_options.push({
18175                 data : o,
18176                 el : opt
18177             });
18178             
18179         }
18180         
18181         this.store.data.each(function(d, rowIndex){
18182             
18183             var html = '';
18184             
18185             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18186                 html = d.data[this.displayField];
18187             }
18188             
18189             var value = '';
18190             
18191             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18192                 value = d.data[this.valueField];
18193             }
18194             
18195             var option = {
18196                 tag: 'option',
18197                 value : value,
18198                 html : html
18199             };
18200             
18201             if(this.value == d.data[this.valueField]){
18202                 option['selected'] = true;
18203             }
18204             
18205             var opt = this.inputEl().createChild(option);
18206             
18207             this.ios_options.push({
18208                 data : d.data,
18209                 el : opt
18210             });
18211             
18212         }, this);
18213         
18214         this.inputEl().on('change', function(){
18215            this.fireEvent('select', this);
18216         }, this);
18217         
18218     },
18219     
18220     clearIOSView: function()
18221     {
18222         this.inputEl().dom.innerHTML = '';
18223         
18224         this.ios_options = [];
18225     },
18226     
18227     setIOSValue: function(v)
18228     {
18229         this.value = v;
18230         
18231         if(!this.ios_options){
18232             return;
18233         }
18234         
18235         Roo.each(this.ios_options, function(opts){
18236            
18237            opts.el.dom.removeAttribute('selected');
18238            
18239            if(opts.data[this.valueField] != v){
18240                return;
18241            }
18242            
18243            opts.el.dom.setAttribute('selected', true);
18244            
18245         }, this);
18246     }
18247
18248     /** 
18249     * @cfg {Boolean} grow 
18250     * @hide 
18251     */
18252     /** 
18253     * @cfg {Number} growMin 
18254     * @hide 
18255     */
18256     /** 
18257     * @cfg {Number} growMax 
18258     * @hide 
18259     */
18260     /**
18261      * @hide
18262      * @method autoSize
18263      */
18264 });
18265
18266 Roo.apply(Roo.bootstrap.ComboBox,  {
18267     
18268     header : {
18269         tag: 'div',
18270         cls: 'modal-header',
18271         cn: [
18272             {
18273                 tag: 'h4',
18274                 cls: 'modal-title'
18275             }
18276         ]
18277     },
18278     
18279     body : {
18280         tag: 'div',
18281         cls: 'modal-body',
18282         cn: [
18283             {
18284                 tag: 'ul',
18285                 cls: 'list-group'
18286             }
18287         ]
18288     },
18289     
18290     listItemRadio : {
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 radio-inline radio radio-info',
18301                 cn: [
18302                     {
18303                         tag: 'input',
18304                         type: 'radio'
18305                     },
18306                     {
18307                         tag: 'label'
18308                     }
18309                 ]
18310             }
18311         ]
18312     },
18313     
18314     listItemCheckbox : {
18315         tag: 'li',
18316         cls: 'list-group-item',
18317         cn: [
18318             {
18319                 tag: 'span',
18320                 cls: 'roo-combobox-list-group-item-value'
18321             },
18322             {
18323                 tag: 'div',
18324                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18325                 cn: [
18326                     {
18327                         tag: 'input',
18328                         type: 'checkbox'
18329                     },
18330                     {
18331                         tag: 'label'
18332                     }
18333                 ]
18334             }
18335         ]
18336     },
18337     
18338     emptyResult : {
18339         tag: 'div',
18340         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18341     },
18342     
18343     footer : {
18344         tag: 'div',
18345         cls: 'modal-footer',
18346         cn: [
18347             {
18348                 tag: 'div',
18349                 cls: 'row',
18350                 cn: [
18351                     {
18352                         tag: 'div',
18353                         cls: 'col-xs-6 text-left',
18354                         cn: {
18355                             tag: 'button',
18356                             cls: 'btn btn-danger roo-touch-view-cancel',
18357                             html: 'Cancel'
18358                         }
18359                     },
18360                     {
18361                         tag: 'div',
18362                         cls: 'col-xs-6 text-right',
18363                         cn: {
18364                             tag: 'button',
18365                             cls: 'btn btn-success roo-touch-view-ok',
18366                             html: 'OK'
18367                         }
18368                     }
18369                 ]
18370             }
18371         ]
18372         
18373     }
18374 });
18375
18376 Roo.apply(Roo.bootstrap.ComboBox,  {
18377     
18378     touchViewTemplate : {
18379         tag: 'div',
18380         cls: 'modal fade roo-combobox-touch-view',
18381         cn: [
18382             {
18383                 tag: 'div',
18384                 cls: 'modal-dialog',
18385                 style : 'position:fixed', // we have to fix position....
18386                 cn: [
18387                     {
18388                         tag: 'div',
18389                         cls: 'modal-content',
18390                         cn: [
18391                             Roo.bootstrap.ComboBox.header,
18392                             Roo.bootstrap.ComboBox.body,
18393                             Roo.bootstrap.ComboBox.footer
18394                         ]
18395                     }
18396                 ]
18397             }
18398         ]
18399     }
18400 });/*
18401  * Based on:
18402  * Ext JS Library 1.1.1
18403  * Copyright(c) 2006-2007, Ext JS, LLC.
18404  *
18405  * Originally Released Under LGPL - original licence link has changed is not relivant.
18406  *
18407  * Fork - LGPL
18408  * <script type="text/javascript">
18409  */
18410
18411 /**
18412  * @class Roo.View
18413  * @extends Roo.util.Observable
18414  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18415  * This class also supports single and multi selection modes. <br>
18416  * Create a data model bound view:
18417  <pre><code>
18418  var store = new Roo.data.Store(...);
18419
18420  var view = new Roo.View({
18421     el : "my-element",
18422     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18423  
18424     singleSelect: true,
18425     selectedClass: "ydataview-selected",
18426     store: store
18427  });
18428
18429  // listen for node click?
18430  view.on("click", function(vw, index, node, e){
18431  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18432  });
18433
18434  // load XML data
18435  dataModel.load("foobar.xml");
18436  </code></pre>
18437  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18438  * <br><br>
18439  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18440  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18441  * 
18442  * Note: old style constructor is still suported (container, template, config)
18443  * 
18444  * @constructor
18445  * Create a new View
18446  * @param {Object} config The config object
18447  * 
18448  */
18449 Roo.View = function(config, depreciated_tpl, depreciated_config){
18450     
18451     this.parent = false;
18452     
18453     if (typeof(depreciated_tpl) == 'undefined') {
18454         // new way.. - universal constructor.
18455         Roo.apply(this, config);
18456         this.el  = Roo.get(this.el);
18457     } else {
18458         // old format..
18459         this.el  = Roo.get(config);
18460         this.tpl = depreciated_tpl;
18461         Roo.apply(this, depreciated_config);
18462     }
18463     this.wrapEl  = this.el.wrap().wrap();
18464     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18465     
18466     
18467     if(typeof(this.tpl) == "string"){
18468         this.tpl = new Roo.Template(this.tpl);
18469     } else {
18470         // support xtype ctors..
18471         this.tpl = new Roo.factory(this.tpl, Roo);
18472     }
18473     
18474     
18475     this.tpl.compile();
18476     
18477     /** @private */
18478     this.addEvents({
18479         /**
18480          * @event beforeclick
18481          * Fires before a click is processed. Returns false to cancel the default action.
18482          * @param {Roo.View} this
18483          * @param {Number} index The index of the target node
18484          * @param {HTMLElement} node The target node
18485          * @param {Roo.EventObject} e The raw event object
18486          */
18487             "beforeclick" : true,
18488         /**
18489          * @event click
18490          * Fires when a template node is clicked.
18491          * @param {Roo.View} this
18492          * @param {Number} index The index of the target node
18493          * @param {HTMLElement} node The target node
18494          * @param {Roo.EventObject} e The raw event object
18495          */
18496             "click" : true,
18497         /**
18498          * @event dblclick
18499          * Fires when a template node is double clicked.
18500          * @param {Roo.View} this
18501          * @param {Number} index The index of the target node
18502          * @param {HTMLElement} node The target node
18503          * @param {Roo.EventObject} e The raw event object
18504          */
18505             "dblclick" : true,
18506         /**
18507          * @event contextmenu
18508          * Fires when a template node is right clicked.
18509          * @param {Roo.View} this
18510          * @param {Number} index The index of the target node
18511          * @param {HTMLElement} node The target node
18512          * @param {Roo.EventObject} e The raw event object
18513          */
18514             "contextmenu" : true,
18515         /**
18516          * @event selectionchange
18517          * Fires when the selected nodes change.
18518          * @param {Roo.View} this
18519          * @param {Array} selections Array of the selected nodes
18520          */
18521             "selectionchange" : true,
18522     
18523         /**
18524          * @event beforeselect
18525          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18526          * @param {Roo.View} this
18527          * @param {HTMLElement} node The node to be selected
18528          * @param {Array} selections Array of currently selected nodes
18529          */
18530             "beforeselect" : true,
18531         /**
18532          * @event preparedata
18533          * Fires on every row to render, to allow you to change the data.
18534          * @param {Roo.View} this
18535          * @param {Object} data to be rendered (change this)
18536          */
18537           "preparedata" : true
18538           
18539           
18540         });
18541
18542
18543
18544     this.el.on({
18545         "click": this.onClick,
18546         "dblclick": this.onDblClick,
18547         "contextmenu": this.onContextMenu,
18548         scope:this
18549     });
18550
18551     this.selections = [];
18552     this.nodes = [];
18553     this.cmp = new Roo.CompositeElementLite([]);
18554     if(this.store){
18555         this.store = Roo.factory(this.store, Roo.data);
18556         this.setStore(this.store, true);
18557     }
18558     
18559     if ( this.footer && this.footer.xtype) {
18560            
18561          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18562         
18563         this.footer.dataSource = this.store;
18564         this.footer.container = fctr;
18565         this.footer = Roo.factory(this.footer, Roo);
18566         fctr.insertFirst(this.el);
18567         
18568         // this is a bit insane - as the paging toolbar seems to detach the el..
18569 //        dom.parentNode.parentNode.parentNode
18570          // they get detached?
18571     }
18572     
18573     
18574     Roo.View.superclass.constructor.call(this);
18575     
18576     
18577 };
18578
18579 Roo.extend(Roo.View, Roo.util.Observable, {
18580     
18581      /**
18582      * @cfg {Roo.data.Store} store Data store to load data from.
18583      */
18584     store : false,
18585     
18586     /**
18587      * @cfg {String|Roo.Element} el The container element.
18588      */
18589     el : '',
18590     
18591     /**
18592      * @cfg {String|Roo.Template} tpl The template used by this View 
18593      */
18594     tpl : false,
18595     /**
18596      * @cfg {String} dataName the named area of the template to use as the data area
18597      *                          Works with domtemplates roo-name="name"
18598      */
18599     dataName: false,
18600     /**
18601      * @cfg {String} selectedClass The css class to add to selected nodes
18602      */
18603     selectedClass : "x-view-selected",
18604      /**
18605      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18606      */
18607     emptyText : "",
18608     
18609     /**
18610      * @cfg {String} text to display on mask (default Loading)
18611      */
18612     mask : false,
18613     /**
18614      * @cfg {Boolean} multiSelect Allow multiple selection
18615      */
18616     multiSelect : false,
18617     /**
18618      * @cfg {Boolean} singleSelect Allow single selection
18619      */
18620     singleSelect:  false,
18621     
18622     /**
18623      * @cfg {Boolean} toggleSelect - selecting 
18624      */
18625     toggleSelect : false,
18626     
18627     /**
18628      * @cfg {Boolean} tickable - selecting 
18629      */
18630     tickable : false,
18631     
18632     /**
18633      * Returns the element this view is bound to.
18634      * @return {Roo.Element}
18635      */
18636     getEl : function(){
18637         return this.wrapEl;
18638     },
18639     
18640     
18641
18642     /**
18643      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18644      */
18645     refresh : function(){
18646         //Roo.log('refresh');
18647         var t = this.tpl;
18648         
18649         // if we are using something like 'domtemplate', then
18650         // the what gets used is:
18651         // t.applySubtemplate(NAME, data, wrapping data..)
18652         // the outer template then get' applied with
18653         //     the store 'extra data'
18654         // and the body get's added to the
18655         //      roo-name="data" node?
18656         //      <span class='roo-tpl-{name}'></span> ?????
18657         
18658         
18659         
18660         this.clearSelections();
18661         this.el.update("");
18662         var html = [];
18663         var records = this.store.getRange();
18664         if(records.length < 1) {
18665             
18666             // is this valid??  = should it render a template??
18667             
18668             this.el.update(this.emptyText);
18669             return;
18670         }
18671         var el = this.el;
18672         if (this.dataName) {
18673             this.el.update(t.apply(this.store.meta)); //????
18674             el = this.el.child('.roo-tpl-' + this.dataName);
18675         }
18676         
18677         for(var i = 0, len = records.length; i < len; i++){
18678             var data = this.prepareData(records[i].data, i, records[i]);
18679             this.fireEvent("preparedata", this, data, i, records[i]);
18680             
18681             var d = Roo.apply({}, data);
18682             
18683             if(this.tickable){
18684                 Roo.apply(d, {'roo-id' : Roo.id()});
18685                 
18686                 var _this = this;
18687             
18688                 Roo.each(this.parent.item, function(item){
18689                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18690                         return;
18691                     }
18692                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18693                 });
18694             }
18695             
18696             html[html.length] = Roo.util.Format.trim(
18697                 this.dataName ?
18698                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18699                     t.apply(d)
18700             );
18701         }
18702         
18703         
18704         
18705         el.update(html.join(""));
18706         this.nodes = el.dom.childNodes;
18707         this.updateIndexes(0);
18708     },
18709     
18710
18711     /**
18712      * Function to override to reformat the data that is sent to
18713      * the template for each node.
18714      * DEPRICATED - use the preparedata event handler.
18715      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18716      * a JSON object for an UpdateManager bound view).
18717      */
18718     prepareData : function(data, index, record)
18719     {
18720         this.fireEvent("preparedata", this, data, index, record);
18721         return data;
18722     },
18723
18724     onUpdate : function(ds, record){
18725         // Roo.log('on update');   
18726         this.clearSelections();
18727         var index = this.store.indexOf(record);
18728         var n = this.nodes[index];
18729         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18730         n.parentNode.removeChild(n);
18731         this.updateIndexes(index, index);
18732     },
18733
18734     
18735     
18736 // --------- FIXME     
18737     onAdd : function(ds, records, index)
18738     {
18739         //Roo.log(['on Add', ds, records, index] );        
18740         this.clearSelections();
18741         if(this.nodes.length == 0){
18742             this.refresh();
18743             return;
18744         }
18745         var n = this.nodes[index];
18746         for(var i = 0, len = records.length; i < len; i++){
18747             var d = this.prepareData(records[i].data, i, records[i]);
18748             if(n){
18749                 this.tpl.insertBefore(n, d);
18750             }else{
18751                 
18752                 this.tpl.append(this.el, d);
18753             }
18754         }
18755         this.updateIndexes(index);
18756     },
18757
18758     onRemove : function(ds, record, index){
18759        // Roo.log('onRemove');
18760         this.clearSelections();
18761         var el = this.dataName  ?
18762             this.el.child('.roo-tpl-' + this.dataName) :
18763             this.el; 
18764         
18765         el.dom.removeChild(this.nodes[index]);
18766         this.updateIndexes(index);
18767     },
18768
18769     /**
18770      * Refresh an individual node.
18771      * @param {Number} index
18772      */
18773     refreshNode : function(index){
18774         this.onUpdate(this.store, this.store.getAt(index));
18775     },
18776
18777     updateIndexes : function(startIndex, endIndex){
18778         var ns = this.nodes;
18779         startIndex = startIndex || 0;
18780         endIndex = endIndex || ns.length - 1;
18781         for(var i = startIndex; i <= endIndex; i++){
18782             ns[i].nodeIndex = i;
18783         }
18784     },
18785
18786     /**
18787      * Changes the data store this view uses and refresh the view.
18788      * @param {Store} store
18789      */
18790     setStore : function(store, initial){
18791         if(!initial && this.store){
18792             this.store.un("datachanged", this.refresh);
18793             this.store.un("add", this.onAdd);
18794             this.store.un("remove", this.onRemove);
18795             this.store.un("update", this.onUpdate);
18796             this.store.un("clear", this.refresh);
18797             this.store.un("beforeload", this.onBeforeLoad);
18798             this.store.un("load", this.onLoad);
18799             this.store.un("loadexception", this.onLoad);
18800         }
18801         if(store){
18802           
18803             store.on("datachanged", this.refresh, this);
18804             store.on("add", this.onAdd, this);
18805             store.on("remove", this.onRemove, this);
18806             store.on("update", this.onUpdate, this);
18807             store.on("clear", this.refresh, this);
18808             store.on("beforeload", this.onBeforeLoad, this);
18809             store.on("load", this.onLoad, this);
18810             store.on("loadexception", this.onLoad, this);
18811         }
18812         
18813         if(store){
18814             this.refresh();
18815         }
18816     },
18817     /**
18818      * onbeforeLoad - masks the loading area.
18819      *
18820      */
18821     onBeforeLoad : function(store,opts)
18822     {
18823          //Roo.log('onBeforeLoad');   
18824         if (!opts.add) {
18825             this.el.update("");
18826         }
18827         this.el.mask(this.mask ? this.mask : "Loading" ); 
18828     },
18829     onLoad : function ()
18830     {
18831         this.el.unmask();
18832     },
18833     
18834
18835     /**
18836      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18837      * @param {HTMLElement} node
18838      * @return {HTMLElement} The template node
18839      */
18840     findItemFromChild : function(node){
18841         var el = this.dataName  ?
18842             this.el.child('.roo-tpl-' + this.dataName,true) :
18843             this.el.dom; 
18844         
18845         if(!node || node.parentNode == el){
18846                     return node;
18847             }
18848             var p = node.parentNode;
18849             while(p && p != el){
18850             if(p.parentNode == el){
18851                 return p;
18852             }
18853             p = p.parentNode;
18854         }
18855             return null;
18856     },
18857
18858     /** @ignore */
18859     onClick : function(e){
18860         var item = this.findItemFromChild(e.getTarget());
18861         if(item){
18862             var index = this.indexOf(item);
18863             if(this.onItemClick(item, index, e) !== false){
18864                 this.fireEvent("click", this, index, item, e);
18865             }
18866         }else{
18867             this.clearSelections();
18868         }
18869     },
18870
18871     /** @ignore */
18872     onContextMenu : function(e){
18873         var item = this.findItemFromChild(e.getTarget());
18874         if(item){
18875             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18876         }
18877     },
18878
18879     /** @ignore */
18880     onDblClick : function(e){
18881         var item = this.findItemFromChild(e.getTarget());
18882         if(item){
18883             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18884         }
18885     },
18886
18887     onItemClick : function(item, index, e)
18888     {
18889         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18890             return false;
18891         }
18892         if (this.toggleSelect) {
18893             var m = this.isSelected(item) ? 'unselect' : 'select';
18894             //Roo.log(m);
18895             var _t = this;
18896             _t[m](item, true, false);
18897             return true;
18898         }
18899         if(this.multiSelect || this.singleSelect){
18900             if(this.multiSelect && e.shiftKey && this.lastSelection){
18901                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18902             }else{
18903                 this.select(item, this.multiSelect && e.ctrlKey);
18904                 this.lastSelection = item;
18905             }
18906             
18907             if(!this.tickable){
18908                 e.preventDefault();
18909             }
18910             
18911         }
18912         return true;
18913     },
18914
18915     /**
18916      * Get the number of selected nodes.
18917      * @return {Number}
18918      */
18919     getSelectionCount : function(){
18920         return this.selections.length;
18921     },
18922
18923     /**
18924      * Get the currently selected nodes.
18925      * @return {Array} An array of HTMLElements
18926      */
18927     getSelectedNodes : function(){
18928         return this.selections;
18929     },
18930
18931     /**
18932      * Get the indexes of the selected nodes.
18933      * @return {Array}
18934      */
18935     getSelectedIndexes : function(){
18936         var indexes = [], s = this.selections;
18937         for(var i = 0, len = s.length; i < len; i++){
18938             indexes.push(s[i].nodeIndex);
18939         }
18940         return indexes;
18941     },
18942
18943     /**
18944      * Clear all selections
18945      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18946      */
18947     clearSelections : function(suppressEvent){
18948         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18949             this.cmp.elements = this.selections;
18950             this.cmp.removeClass(this.selectedClass);
18951             this.selections = [];
18952             if(!suppressEvent){
18953                 this.fireEvent("selectionchange", this, this.selections);
18954             }
18955         }
18956     },
18957
18958     /**
18959      * Returns true if the passed node is selected
18960      * @param {HTMLElement/Number} node The node or node index
18961      * @return {Boolean}
18962      */
18963     isSelected : function(node){
18964         var s = this.selections;
18965         if(s.length < 1){
18966             return false;
18967         }
18968         node = this.getNode(node);
18969         return s.indexOf(node) !== -1;
18970     },
18971
18972     /**
18973      * Selects nodes.
18974      * @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
18975      * @param {Boolean} keepExisting (optional) true to keep existing selections
18976      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18977      */
18978     select : function(nodeInfo, keepExisting, suppressEvent){
18979         if(nodeInfo instanceof Array){
18980             if(!keepExisting){
18981                 this.clearSelections(true);
18982             }
18983             for(var i = 0, len = nodeInfo.length; i < len; i++){
18984                 this.select(nodeInfo[i], true, true);
18985             }
18986             return;
18987         } 
18988         var node = this.getNode(nodeInfo);
18989         if(!node || this.isSelected(node)){
18990             return; // already selected.
18991         }
18992         if(!keepExisting){
18993             this.clearSelections(true);
18994         }
18995         
18996         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18997             Roo.fly(node).addClass(this.selectedClass);
18998             this.selections.push(node);
18999             if(!suppressEvent){
19000                 this.fireEvent("selectionchange", this, this.selections);
19001             }
19002         }
19003         
19004         
19005     },
19006       /**
19007      * Unselects nodes.
19008      * @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
19009      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19010      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19011      */
19012     unselect : function(nodeInfo, keepExisting, suppressEvent)
19013     {
19014         if(nodeInfo instanceof Array){
19015             Roo.each(this.selections, function(s) {
19016                 this.unselect(s, nodeInfo);
19017             }, this);
19018             return;
19019         }
19020         var node = this.getNode(nodeInfo);
19021         if(!node || !this.isSelected(node)){
19022             //Roo.log("not selected");
19023             return; // not selected.
19024         }
19025         // fireevent???
19026         var ns = [];
19027         Roo.each(this.selections, function(s) {
19028             if (s == node ) {
19029                 Roo.fly(node).removeClass(this.selectedClass);
19030
19031                 return;
19032             }
19033             ns.push(s);
19034         },this);
19035         
19036         this.selections= ns;
19037         this.fireEvent("selectionchange", this, this.selections);
19038     },
19039
19040     /**
19041      * Gets a template node.
19042      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19043      * @return {HTMLElement} The node or null if it wasn't found
19044      */
19045     getNode : function(nodeInfo){
19046         if(typeof nodeInfo == "string"){
19047             return document.getElementById(nodeInfo);
19048         }else if(typeof nodeInfo == "number"){
19049             return this.nodes[nodeInfo];
19050         }
19051         return nodeInfo;
19052     },
19053
19054     /**
19055      * Gets a range template nodes.
19056      * @param {Number} startIndex
19057      * @param {Number} endIndex
19058      * @return {Array} An array of nodes
19059      */
19060     getNodes : function(start, end){
19061         var ns = this.nodes;
19062         start = start || 0;
19063         end = typeof end == "undefined" ? ns.length - 1 : end;
19064         var nodes = [];
19065         if(start <= end){
19066             for(var i = start; i <= end; i++){
19067                 nodes.push(ns[i]);
19068             }
19069         } else{
19070             for(var i = start; i >= end; i--){
19071                 nodes.push(ns[i]);
19072             }
19073         }
19074         return nodes;
19075     },
19076
19077     /**
19078      * Finds the index of the passed node
19079      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19080      * @return {Number} The index of the node or -1
19081      */
19082     indexOf : function(node){
19083         node = this.getNode(node);
19084         if(typeof node.nodeIndex == "number"){
19085             return node.nodeIndex;
19086         }
19087         var ns = this.nodes;
19088         for(var i = 0, len = ns.length; i < len; i++){
19089             if(ns[i] == node){
19090                 return i;
19091             }
19092         }
19093         return -1;
19094     }
19095 });
19096 /*
19097  * - LGPL
19098  *
19099  * based on jquery fullcalendar
19100  * 
19101  */
19102
19103 Roo.bootstrap = Roo.bootstrap || {};
19104 /**
19105  * @class Roo.bootstrap.Calendar
19106  * @extends Roo.bootstrap.Component
19107  * Bootstrap Calendar class
19108  * @cfg {Boolean} loadMask (true|false) default false
19109  * @cfg {Object} header generate the user specific header of the calendar, default false
19110
19111  * @constructor
19112  * Create a new Container
19113  * @param {Object} config The config object
19114  */
19115
19116
19117
19118 Roo.bootstrap.Calendar = function(config){
19119     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19120      this.addEvents({
19121         /**
19122              * @event select
19123              * Fires when a date is selected
19124              * @param {DatePicker} this
19125              * @param {Date} date The selected date
19126              */
19127         'select': true,
19128         /**
19129              * @event monthchange
19130              * Fires when the displayed month changes 
19131              * @param {DatePicker} this
19132              * @param {Date} date The selected month
19133              */
19134         'monthchange': true,
19135         /**
19136              * @event evententer
19137              * Fires when mouse over an event
19138              * @param {Calendar} this
19139              * @param {event} Event
19140              */
19141         'evententer': true,
19142         /**
19143              * @event eventleave
19144              * Fires when the mouse leaves an
19145              * @param {Calendar} this
19146              * @param {event}
19147              */
19148         'eventleave': true,
19149         /**
19150              * @event eventclick
19151              * Fires when the mouse click an
19152              * @param {Calendar} this
19153              * @param {event}
19154              */
19155         'eventclick': true
19156         
19157     });
19158
19159 };
19160
19161 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19162     
19163      /**
19164      * @cfg {Number} startDay
19165      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19166      */
19167     startDay : 0,
19168     
19169     loadMask : false,
19170     
19171     header : false,
19172       
19173     getAutoCreate : function(){
19174         
19175         
19176         var fc_button = function(name, corner, style, content ) {
19177             return Roo.apply({},{
19178                 tag : 'span',
19179                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19180                          (corner.length ?
19181                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19182                             ''
19183                         ),
19184                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19185                 unselectable: 'on'
19186             });
19187         };
19188         
19189         var header = {};
19190         
19191         if(!this.header){
19192             header = {
19193                 tag : 'table',
19194                 cls : 'fc-header',
19195                 style : 'width:100%',
19196                 cn : [
19197                     {
19198                         tag: 'tr',
19199                         cn : [
19200                             {
19201                                 tag : 'td',
19202                                 cls : 'fc-header-left',
19203                                 cn : [
19204                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19205                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19206                                     { tag: 'span', cls: 'fc-header-space' },
19207                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19208
19209
19210                                 ]
19211                             },
19212
19213                             {
19214                                 tag : 'td',
19215                                 cls : 'fc-header-center',
19216                                 cn : [
19217                                     {
19218                                         tag: 'span',
19219                                         cls: 'fc-header-title',
19220                                         cn : {
19221                                             tag: 'H2',
19222                                             html : 'month / year'
19223                                         }
19224                                     }
19225
19226                                 ]
19227                             },
19228                             {
19229                                 tag : 'td',
19230                                 cls : 'fc-header-right',
19231                                 cn : [
19232                               /*      fc_button('month', 'left', '', 'month' ),
19233                                     fc_button('week', '', '', 'week' ),
19234                                     fc_button('day', 'right', '', 'day' )
19235                                 */    
19236
19237                                 ]
19238                             }
19239
19240                         ]
19241                     }
19242                 ]
19243             };
19244         }
19245         
19246         header = this.header;
19247         
19248        
19249         var cal_heads = function() {
19250             var ret = [];
19251             // fixme - handle this.
19252             
19253             for (var i =0; i < Date.dayNames.length; i++) {
19254                 var d = Date.dayNames[i];
19255                 ret.push({
19256                     tag: 'th',
19257                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19258                     html : d.substring(0,3)
19259                 });
19260                 
19261             }
19262             ret[0].cls += ' fc-first';
19263             ret[6].cls += ' fc-last';
19264             return ret;
19265         };
19266         var cal_cell = function(n) {
19267             return  {
19268                 tag: 'td',
19269                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19270                 cn : [
19271                     {
19272                         cn : [
19273                             {
19274                                 cls: 'fc-day-number',
19275                                 html: 'D'
19276                             },
19277                             {
19278                                 cls: 'fc-day-content',
19279                              
19280                                 cn : [
19281                                      {
19282                                         style: 'position: relative;' // height: 17px;
19283                                     }
19284                                 ]
19285                             }
19286                             
19287                             
19288                         ]
19289                     }
19290                 ]
19291                 
19292             }
19293         };
19294         var cal_rows = function() {
19295             
19296             var ret = [];
19297             for (var r = 0; r < 6; r++) {
19298                 var row= {
19299                     tag : 'tr',
19300                     cls : 'fc-week',
19301                     cn : []
19302                 };
19303                 
19304                 for (var i =0; i < Date.dayNames.length; i++) {
19305                     var d = Date.dayNames[i];
19306                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19307
19308                 }
19309                 row.cn[0].cls+=' fc-first';
19310                 row.cn[0].cn[0].style = 'min-height:90px';
19311                 row.cn[6].cls+=' fc-last';
19312                 ret.push(row);
19313                 
19314             }
19315             ret[0].cls += ' fc-first';
19316             ret[4].cls += ' fc-prev-last';
19317             ret[5].cls += ' fc-last';
19318             return ret;
19319             
19320         };
19321         
19322         var cal_table = {
19323             tag: 'table',
19324             cls: 'fc-border-separate',
19325             style : 'width:100%',
19326             cellspacing  : 0,
19327             cn : [
19328                 { 
19329                     tag: 'thead',
19330                     cn : [
19331                         { 
19332                             tag: 'tr',
19333                             cls : 'fc-first fc-last',
19334                             cn : cal_heads()
19335                         }
19336                     ]
19337                 },
19338                 { 
19339                     tag: 'tbody',
19340                     cn : cal_rows()
19341                 }
19342                   
19343             ]
19344         };
19345          
19346          var cfg = {
19347             cls : 'fc fc-ltr',
19348             cn : [
19349                 header,
19350                 {
19351                     cls : 'fc-content',
19352                     style : "position: relative;",
19353                     cn : [
19354                         {
19355                             cls : 'fc-view fc-view-month fc-grid',
19356                             style : 'position: relative',
19357                             unselectable : 'on',
19358                             cn : [
19359                                 {
19360                                     cls : 'fc-event-container',
19361                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19362                                 },
19363                                 cal_table
19364                             ]
19365                         }
19366                     ]
19367     
19368                 }
19369            ] 
19370             
19371         };
19372         
19373          
19374         
19375         return cfg;
19376     },
19377     
19378     
19379     initEvents : function()
19380     {
19381         if(!this.store){
19382             throw "can not find store for calendar";
19383         }
19384         
19385         var mark = {
19386             tag: "div",
19387             cls:"x-dlg-mask",
19388             style: "text-align:center",
19389             cn: [
19390                 {
19391                     tag: "div",
19392                     style: "background-color:white;width:50%;margin:250 auto",
19393                     cn: [
19394                         {
19395                             tag: "img",
19396                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19397                         },
19398                         {
19399                             tag: "span",
19400                             html: "Loading"
19401                         }
19402                         
19403                     ]
19404                 }
19405             ]
19406         };
19407         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19408         
19409         var size = this.el.select('.fc-content', true).first().getSize();
19410         this.maskEl.setSize(size.width, size.height);
19411         this.maskEl.enableDisplayMode("block");
19412         if(!this.loadMask){
19413             this.maskEl.hide();
19414         }
19415         
19416         this.store = Roo.factory(this.store, Roo.data);
19417         this.store.on('load', this.onLoad, this);
19418         this.store.on('beforeload', this.onBeforeLoad, this);
19419         
19420         this.resize();
19421         
19422         this.cells = this.el.select('.fc-day',true);
19423         //Roo.log(this.cells);
19424         this.textNodes = this.el.query('.fc-day-number');
19425         this.cells.addClassOnOver('fc-state-hover');
19426         
19427         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19428         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19429         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19430         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19431         
19432         this.on('monthchange', this.onMonthChange, this);
19433         
19434         this.update(new Date().clearTime());
19435     },
19436     
19437     resize : function() {
19438         var sz  = this.el.getSize();
19439         
19440         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19441         this.el.select('.fc-day-content div',true).setHeight(34);
19442     },
19443     
19444     
19445     // private
19446     showPrevMonth : function(e){
19447         this.update(this.activeDate.add("mo", -1));
19448     },
19449     showToday : function(e){
19450         this.update(new Date().clearTime());
19451     },
19452     // private
19453     showNextMonth : function(e){
19454         this.update(this.activeDate.add("mo", 1));
19455     },
19456
19457     // private
19458     showPrevYear : function(){
19459         this.update(this.activeDate.add("y", -1));
19460     },
19461
19462     // private
19463     showNextYear : function(){
19464         this.update(this.activeDate.add("y", 1));
19465     },
19466
19467     
19468    // private
19469     update : function(date)
19470     {
19471         var vd = this.activeDate;
19472         this.activeDate = date;
19473 //        if(vd && this.el){
19474 //            var t = date.getTime();
19475 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19476 //                Roo.log('using add remove');
19477 //                
19478 //                this.fireEvent('monthchange', this, date);
19479 //                
19480 //                this.cells.removeClass("fc-state-highlight");
19481 //                this.cells.each(function(c){
19482 //                   if(c.dateValue == t){
19483 //                       c.addClass("fc-state-highlight");
19484 //                       setTimeout(function(){
19485 //                            try{c.dom.firstChild.focus();}catch(e){}
19486 //                       }, 50);
19487 //                       return false;
19488 //                   }
19489 //                   return true;
19490 //                });
19491 //                return;
19492 //            }
19493 //        }
19494         
19495         var days = date.getDaysInMonth();
19496         
19497         var firstOfMonth = date.getFirstDateOfMonth();
19498         var startingPos = firstOfMonth.getDay()-this.startDay;
19499         
19500         if(startingPos < this.startDay){
19501             startingPos += 7;
19502         }
19503         
19504         var pm = date.add(Date.MONTH, -1);
19505         var prevStart = pm.getDaysInMonth()-startingPos;
19506 //        
19507         this.cells = this.el.select('.fc-day',true);
19508         this.textNodes = this.el.query('.fc-day-number');
19509         this.cells.addClassOnOver('fc-state-hover');
19510         
19511         var cells = this.cells.elements;
19512         var textEls = this.textNodes;
19513         
19514         Roo.each(cells, function(cell){
19515             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19516         });
19517         
19518         days += startingPos;
19519
19520         // convert everything to numbers so it's fast
19521         var day = 86400000;
19522         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19523         //Roo.log(d);
19524         //Roo.log(pm);
19525         //Roo.log(prevStart);
19526         
19527         var today = new Date().clearTime().getTime();
19528         var sel = date.clearTime().getTime();
19529         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19530         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19531         var ddMatch = this.disabledDatesRE;
19532         var ddText = this.disabledDatesText;
19533         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19534         var ddaysText = this.disabledDaysText;
19535         var format = this.format;
19536         
19537         var setCellClass = function(cal, cell){
19538             cell.row = 0;
19539             cell.events = [];
19540             cell.more = [];
19541             //Roo.log('set Cell Class');
19542             cell.title = "";
19543             var t = d.getTime();
19544             
19545             //Roo.log(d);
19546             
19547             cell.dateValue = t;
19548             if(t == today){
19549                 cell.className += " fc-today";
19550                 cell.className += " fc-state-highlight";
19551                 cell.title = cal.todayText;
19552             }
19553             if(t == sel){
19554                 // disable highlight in other month..
19555                 //cell.className += " fc-state-highlight";
19556                 
19557             }
19558             // disabling
19559             if(t < min) {
19560                 cell.className = " fc-state-disabled";
19561                 cell.title = cal.minText;
19562                 return;
19563             }
19564             if(t > max) {
19565                 cell.className = " fc-state-disabled";
19566                 cell.title = cal.maxText;
19567                 return;
19568             }
19569             if(ddays){
19570                 if(ddays.indexOf(d.getDay()) != -1){
19571                     cell.title = ddaysText;
19572                     cell.className = " fc-state-disabled";
19573                 }
19574             }
19575             if(ddMatch && format){
19576                 var fvalue = d.dateFormat(format);
19577                 if(ddMatch.test(fvalue)){
19578                     cell.title = ddText.replace("%0", fvalue);
19579                     cell.className = " fc-state-disabled";
19580                 }
19581             }
19582             
19583             if (!cell.initialClassName) {
19584                 cell.initialClassName = cell.dom.className;
19585             }
19586             
19587             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19588         };
19589
19590         var i = 0;
19591         
19592         for(; i < startingPos; i++) {
19593             textEls[i].innerHTML = (++prevStart);
19594             d.setDate(d.getDate()+1);
19595             
19596             cells[i].className = "fc-past fc-other-month";
19597             setCellClass(this, cells[i]);
19598         }
19599         
19600         var intDay = 0;
19601         
19602         for(; i < days; i++){
19603             intDay = i - startingPos + 1;
19604             textEls[i].innerHTML = (intDay);
19605             d.setDate(d.getDate()+1);
19606             
19607             cells[i].className = ''; // "x-date-active";
19608             setCellClass(this, cells[i]);
19609         }
19610         var extraDays = 0;
19611         
19612         for(; i < 42; i++) {
19613             textEls[i].innerHTML = (++extraDays);
19614             d.setDate(d.getDate()+1);
19615             
19616             cells[i].className = "fc-future fc-other-month";
19617             setCellClass(this, cells[i]);
19618         }
19619         
19620         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19621         
19622         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19623         
19624         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19625         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19626         
19627         if(totalRows != 6){
19628             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19629             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19630         }
19631         
19632         this.fireEvent('monthchange', this, date);
19633         
19634         
19635         /*
19636         if(!this.internalRender){
19637             var main = this.el.dom.firstChild;
19638             var w = main.offsetWidth;
19639             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19640             Roo.fly(main).setWidth(w);
19641             this.internalRender = true;
19642             // opera does not respect the auto grow header center column
19643             // then, after it gets a width opera refuses to recalculate
19644             // without a second pass
19645             if(Roo.isOpera && !this.secondPass){
19646                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19647                 this.secondPass = true;
19648                 this.update.defer(10, this, [date]);
19649             }
19650         }
19651         */
19652         
19653     },
19654     
19655     findCell : function(dt) {
19656         dt = dt.clearTime().getTime();
19657         var ret = false;
19658         this.cells.each(function(c){
19659             //Roo.log("check " +c.dateValue + '?=' + dt);
19660             if(c.dateValue == dt){
19661                 ret = c;
19662                 return false;
19663             }
19664             return true;
19665         });
19666         
19667         return ret;
19668     },
19669     
19670     findCells : function(ev) {
19671         var s = ev.start.clone().clearTime().getTime();
19672        // Roo.log(s);
19673         var e= ev.end.clone().clearTime().getTime();
19674        // Roo.log(e);
19675         var ret = [];
19676         this.cells.each(function(c){
19677              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19678             
19679             if(c.dateValue > e){
19680                 return ;
19681             }
19682             if(c.dateValue < s){
19683                 return ;
19684             }
19685             ret.push(c);
19686         });
19687         
19688         return ret;    
19689     },
19690     
19691 //    findBestRow: function(cells)
19692 //    {
19693 //        var ret = 0;
19694 //        
19695 //        for (var i =0 ; i < cells.length;i++) {
19696 //            ret  = Math.max(cells[i].rows || 0,ret);
19697 //        }
19698 //        return ret;
19699 //        
19700 //    },
19701     
19702     
19703     addItem : function(ev)
19704     {
19705         // look for vertical location slot in
19706         var cells = this.findCells(ev);
19707         
19708 //        ev.row = this.findBestRow(cells);
19709         
19710         // work out the location.
19711         
19712         var crow = false;
19713         var rows = [];
19714         for(var i =0; i < cells.length; i++) {
19715             
19716             cells[i].row = cells[0].row;
19717             
19718             if(i == 0){
19719                 cells[i].row = cells[i].row + 1;
19720             }
19721             
19722             if (!crow) {
19723                 crow = {
19724                     start : cells[i],
19725                     end :  cells[i]
19726                 };
19727                 continue;
19728             }
19729             if (crow.start.getY() == cells[i].getY()) {
19730                 // on same row.
19731                 crow.end = cells[i];
19732                 continue;
19733             }
19734             // different row.
19735             rows.push(crow);
19736             crow = {
19737                 start: cells[i],
19738                 end : cells[i]
19739             };
19740             
19741         }
19742         
19743         rows.push(crow);
19744         ev.els = [];
19745         ev.rows = rows;
19746         ev.cells = cells;
19747         
19748         cells[0].events.push(ev);
19749         
19750         this.calevents.push(ev);
19751     },
19752     
19753     clearEvents: function() {
19754         
19755         if(!this.calevents){
19756             return;
19757         }
19758         
19759         Roo.each(this.cells.elements, function(c){
19760             c.row = 0;
19761             c.events = [];
19762             c.more = [];
19763         });
19764         
19765         Roo.each(this.calevents, function(e) {
19766             Roo.each(e.els, function(el) {
19767                 el.un('mouseenter' ,this.onEventEnter, this);
19768                 el.un('mouseleave' ,this.onEventLeave, this);
19769                 el.remove();
19770             },this);
19771         },this);
19772         
19773         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19774             e.remove();
19775         });
19776         
19777     },
19778     
19779     renderEvents: function()
19780     {   
19781         var _this = this;
19782         
19783         this.cells.each(function(c) {
19784             
19785             if(c.row < 5){
19786                 return;
19787             }
19788             
19789             var ev = c.events;
19790             
19791             var r = 4;
19792             if(c.row != c.events.length){
19793                 r = 4 - (4 - (c.row - c.events.length));
19794             }
19795             
19796             c.events = ev.slice(0, r);
19797             c.more = ev.slice(r);
19798             
19799             if(c.more.length && c.more.length == 1){
19800                 c.events.push(c.more.pop());
19801             }
19802             
19803             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19804             
19805         });
19806             
19807         this.cells.each(function(c) {
19808             
19809             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19810             
19811             
19812             for (var e = 0; e < c.events.length; e++){
19813                 var ev = c.events[e];
19814                 var rows = ev.rows;
19815                 
19816                 for(var i = 0; i < rows.length; i++) {
19817                 
19818                     // how many rows should it span..
19819
19820                     var  cfg = {
19821                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19822                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19823
19824                         unselectable : "on",
19825                         cn : [
19826                             {
19827                                 cls: 'fc-event-inner',
19828                                 cn : [
19829     //                                {
19830     //                                  tag:'span',
19831     //                                  cls: 'fc-event-time',
19832     //                                  html : cells.length > 1 ? '' : ev.time
19833     //                                },
19834                                     {
19835                                       tag:'span',
19836                                       cls: 'fc-event-title',
19837                                       html : String.format('{0}', ev.title)
19838                                     }
19839
19840
19841                                 ]
19842                             },
19843                             {
19844                                 cls: 'ui-resizable-handle ui-resizable-e',
19845                                 html : '&nbsp;&nbsp;&nbsp'
19846                             }
19847
19848                         ]
19849                     };
19850
19851                     if (i == 0) {
19852                         cfg.cls += ' fc-event-start';
19853                     }
19854                     if ((i+1) == rows.length) {
19855                         cfg.cls += ' fc-event-end';
19856                     }
19857
19858                     var ctr = _this.el.select('.fc-event-container',true).first();
19859                     var cg = ctr.createChild(cfg);
19860
19861                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19862                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19863
19864                     var r = (c.more.length) ? 1 : 0;
19865                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19866                     cg.setWidth(ebox.right - sbox.x -2);
19867
19868                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19869                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19870                     cg.on('click', _this.onEventClick, _this, ev);
19871
19872                     ev.els.push(cg);
19873                     
19874                 }
19875                 
19876             }
19877             
19878             
19879             if(c.more.length){
19880                 var  cfg = {
19881                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19882                     style : 'position: absolute',
19883                     unselectable : "on",
19884                     cn : [
19885                         {
19886                             cls: 'fc-event-inner',
19887                             cn : [
19888                                 {
19889                                   tag:'span',
19890                                   cls: 'fc-event-title',
19891                                   html : 'More'
19892                                 }
19893
19894
19895                             ]
19896                         },
19897                         {
19898                             cls: 'ui-resizable-handle ui-resizable-e',
19899                             html : '&nbsp;&nbsp;&nbsp'
19900                         }
19901
19902                     ]
19903                 };
19904
19905                 var ctr = _this.el.select('.fc-event-container',true).first();
19906                 var cg = ctr.createChild(cfg);
19907
19908                 var sbox = c.select('.fc-day-content',true).first().getBox();
19909                 var ebox = c.select('.fc-day-content',true).first().getBox();
19910                 //Roo.log(cg);
19911                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19912                 cg.setWidth(ebox.right - sbox.x -2);
19913
19914                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19915                 
19916             }
19917             
19918         });
19919         
19920         
19921         
19922     },
19923     
19924     onEventEnter: function (e, el,event,d) {
19925         this.fireEvent('evententer', this, el, event);
19926     },
19927     
19928     onEventLeave: function (e, el,event,d) {
19929         this.fireEvent('eventleave', this, el, event);
19930     },
19931     
19932     onEventClick: function (e, el,event,d) {
19933         this.fireEvent('eventclick', this, el, event);
19934     },
19935     
19936     onMonthChange: function () {
19937         this.store.load();
19938     },
19939     
19940     onMoreEventClick: function(e, el, more)
19941     {
19942         var _this = this;
19943         
19944         this.calpopover.placement = 'right';
19945         this.calpopover.setTitle('More');
19946         
19947         this.calpopover.setContent('');
19948         
19949         var ctr = this.calpopover.el.select('.popover-content', true).first();
19950         
19951         Roo.each(more, function(m){
19952             var cfg = {
19953                 cls : 'fc-event-hori fc-event-draggable',
19954                 html : m.title
19955             };
19956             var cg = ctr.createChild(cfg);
19957             
19958             cg.on('click', _this.onEventClick, _this, m);
19959         });
19960         
19961         this.calpopover.show(el);
19962         
19963         
19964     },
19965     
19966     onLoad: function () 
19967     {   
19968         this.calevents = [];
19969         var cal = this;
19970         
19971         if(this.store.getCount() > 0){
19972             this.store.data.each(function(d){
19973                cal.addItem({
19974                     id : d.data.id,
19975                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19976                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19977                     time : d.data.start_time,
19978                     title : d.data.title,
19979                     description : d.data.description,
19980                     venue : d.data.venue
19981                 });
19982             });
19983         }
19984         
19985         this.renderEvents();
19986         
19987         if(this.calevents.length && this.loadMask){
19988             this.maskEl.hide();
19989         }
19990     },
19991     
19992     onBeforeLoad: function()
19993     {
19994         this.clearEvents();
19995         if(this.loadMask){
19996             this.maskEl.show();
19997         }
19998     }
19999 });
20000
20001  
20002  /*
20003  * - LGPL
20004  *
20005  * element
20006  * 
20007  */
20008
20009 /**
20010  * @class Roo.bootstrap.Popover
20011  * @extends Roo.bootstrap.Component
20012  * Bootstrap Popover class
20013  * @cfg {String} html contents of the popover   (or false to use children..)
20014  * @cfg {String} title of popover (or false to hide)
20015  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20016  * @cfg {String} trigger click || hover (or false to trigger manually)
20017  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20018  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20019  *      - if false and it has a 'parent' then it will be automatically added to that element
20020  *      - if string - Roo.get  will be called 
20021  * @cfg {Number} delay - delay before showing
20022  
20023  * @constructor
20024  * Create a new Popover
20025  * @param {Object} config The config object
20026  */
20027
20028 Roo.bootstrap.Popover = function(config){
20029     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20030     
20031     this.addEvents({
20032         // raw events
20033          /**
20034          * @event show
20035          * After the popover show
20036          * 
20037          * @param {Roo.bootstrap.Popover} this
20038          */
20039         "show" : true,
20040         /**
20041          * @event hide
20042          * After the popover hide
20043          * 
20044          * @param {Roo.bootstrap.Popover} this
20045          */
20046         "hide" : true
20047     });
20048 };
20049
20050 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20051     
20052     title: false,
20053     html: false,
20054     
20055     placement : 'right',
20056     trigger : 'hover', // hover
20057     modal : false,
20058     delay : 0,
20059     
20060     over: false,
20061     
20062     can_build_overlaid : false,
20063     
20064     maskEl : false, // the mask element
20065     headerEl : false,
20066     contentEl : false,
20067     alignEl : false, // when show is called with an element - this get's stored.
20068     
20069     getChildContainer : function()
20070     {
20071         return this.contentEl;
20072         
20073     },
20074     getPopoverHeader : function()
20075     {
20076         this.title = true; // flag not to hide it..
20077         this.headerEl.addClass('p-0');
20078         return this.headerEl
20079     },
20080     
20081     
20082     getAutoCreate : function(){
20083          
20084         var cfg = {
20085            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20086            style: 'display:block',
20087            cn : [
20088                 {
20089                     cls : 'arrow'
20090                 },
20091                 {
20092                     cls : 'popover-inner ',
20093                     cn : [
20094                         {
20095                             tag: 'h3',
20096                             cls: 'popover-title popover-header',
20097                             html : this.title === false ? '' : this.title
20098                         },
20099                         {
20100                             cls : 'popover-content popover-body '  + (this.cls || ''),
20101                             html : this.html || ''
20102                         }
20103                     ]
20104                     
20105                 }
20106            ]
20107         };
20108         
20109         return cfg;
20110     },
20111     /**
20112      * @param {string} the title
20113      */
20114     setTitle: function(str)
20115     {
20116         this.title = str;
20117         if (this.el) {
20118             this.headerEl.dom.innerHTML = str;
20119         }
20120         
20121     },
20122     /**
20123      * @param {string} the body content
20124      */
20125     setContent: function(str)
20126     {
20127         this.html = str;
20128         if (this.contentEl) {
20129             this.contentEl.dom.innerHTML = str;
20130         }
20131         
20132     },
20133     // as it get's added to the bottom of the page.
20134     onRender : function(ct, position)
20135     {
20136         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20137         
20138         
20139         
20140         if(!this.el){
20141             var cfg = Roo.apply({},  this.getAutoCreate());
20142             cfg.id = Roo.id();
20143             
20144             if (this.cls) {
20145                 cfg.cls += ' ' + this.cls;
20146             }
20147             if (this.style) {
20148                 cfg.style = this.style;
20149             }
20150             //Roo.log("adding to ");
20151             this.el = Roo.get(document.body).createChild(cfg, position);
20152 //            Roo.log(this.el);
20153         }
20154         
20155         this.contentEl = this.el.select('.popover-content',true).first();
20156         this.headerEl =  this.el.select('.popover-title',true).first();
20157         
20158         var nitems = [];
20159         if(typeof(this.items) != 'undefined'){
20160             var items = this.items;
20161             delete this.items;
20162
20163             for(var i =0;i < items.length;i++) {
20164                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20165             }
20166         }
20167
20168         this.items = nitems;
20169         
20170         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20171         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20172         
20173         
20174         
20175         this.initEvents();
20176     },
20177     
20178     resizeMask : function()
20179     {
20180         this.maskEl.setSize(
20181             Roo.lib.Dom.getViewWidth(true),
20182             Roo.lib.Dom.getViewHeight(true)
20183         );
20184     },
20185     
20186     initEvents : function()
20187     {
20188         
20189         if (!this.modal) { 
20190             Roo.bootstrap.Popover.register(this);
20191         }
20192          
20193         this.arrowEl = this.el.select('.arrow',true).first();
20194         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20195         this.el.enableDisplayMode('block');
20196         this.el.hide();
20197  
20198         
20199         if (this.over === false && !this.parent()) {
20200             return; 
20201         }
20202         if (this.triggers === false) {
20203             return;
20204         }
20205          
20206         // support parent
20207         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20208         var triggers = this.trigger ? this.trigger.split(' ') : [];
20209         Roo.each(triggers, function(trigger) {
20210         
20211             if (trigger == 'click') {
20212                 on_el.on('click', this.toggle, this);
20213             } else if (trigger != 'manual') {
20214                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20215                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20216       
20217                 on_el.on(eventIn  ,this.enter, this);
20218                 on_el.on(eventOut, this.leave, this);
20219             }
20220         }, this);
20221     },
20222     
20223     
20224     // private
20225     timeout : null,
20226     hoverState : null,
20227     
20228     toggle : function () {
20229         this.hoverState == 'in' ? this.leave() : this.enter();
20230     },
20231     
20232     enter : function () {
20233         
20234         clearTimeout(this.timeout);
20235     
20236         this.hoverState = 'in';
20237     
20238         if (!this.delay || !this.delay.show) {
20239             this.show();
20240             return;
20241         }
20242         var _t = this;
20243         this.timeout = setTimeout(function () {
20244             if (_t.hoverState == 'in') {
20245                 _t.show();
20246             }
20247         }, this.delay.show)
20248     },
20249     
20250     leave : function() {
20251         clearTimeout(this.timeout);
20252     
20253         this.hoverState = 'out';
20254     
20255         if (!this.delay || !this.delay.hide) {
20256             this.hide();
20257             return;
20258         }
20259         var _t = this;
20260         this.timeout = setTimeout(function () {
20261             if (_t.hoverState == 'out') {
20262                 _t.hide();
20263             }
20264         }, this.delay.hide)
20265     },
20266     /**
20267      * Show the popover
20268      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20269      * @param {string} (left|right|top|bottom) position
20270      */
20271     show : function (on_el, placement)
20272     {
20273         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20274         on_el = on_el || false; // default to false
20275          
20276         if (!on_el) {
20277             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20278                 on_el = this.parent().el;
20279             } else if (this.over) {
20280                 on_el = Roo.get(this.over);
20281             }
20282             
20283         }
20284         
20285         this.alignEl = Roo.get( on_el );
20286
20287         if (!this.el) {
20288             this.render(document.body);
20289         }
20290         
20291         
20292          
20293         
20294         if (this.title === false) {
20295             this.headerEl.hide();
20296         }
20297         
20298        
20299         this.el.show();
20300         this.el.dom.style.display = 'block';
20301          
20302  
20303         if (this.alignEl) {
20304             this.updatePosition(this.placement, true);
20305              
20306         } else {
20307             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20308             var es = this.el.getSize();
20309             var x = Roo.lib.Dom.getViewWidth()/2;
20310             var y = Roo.lib.Dom.getViewHeight()/2;
20311             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20312             
20313         }
20314
20315         
20316         //var arrow = this.el.select('.arrow',true).first();
20317         //arrow.set(align[2], 
20318         
20319         this.el.addClass('in');
20320         
20321          
20322         
20323         this.hoverState = 'in';
20324         
20325         if (this.modal) {
20326             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20327             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20328             this.maskEl.dom.style.display = 'block';
20329             this.maskEl.addClass('show');
20330         }
20331         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20332  
20333         this.fireEvent('show', this);
20334         
20335     },
20336     /**
20337      * fire this manually after loading a grid in the table for example
20338      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20339      * @param {Boolean} try and move it if we cant get right position.
20340      */
20341     updatePosition : function(placement, try_move)
20342     {
20343         // allow for calling with no parameters
20344         placement = placement   ? placement :  this.placement;
20345         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20346         
20347         this.el.removeClass([
20348             'fade','top','bottom', 'left', 'right','in',
20349             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20350         ]);
20351         this.el.addClass(placement + ' bs-popover-' + placement);
20352         
20353         if (!this.alignEl ) {
20354             return false;
20355         }
20356         
20357         switch (placement) {
20358             case 'right':
20359                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20360                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20361                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20362                     //normal display... or moved up/down.
20363                     this.el.setXY(offset);
20364                     var xy = this.alignEl.getAnchorXY('tr', false);
20365                     xy[0]+=2;xy[1]+=5;
20366                     this.arrowEl.setXY(xy);
20367                     return true;
20368                 }
20369                 // continue through...
20370                 return this.updatePosition('left', false);
20371                 
20372             
20373             case 'left':
20374                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20375                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20376                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20377                     //normal display... or moved up/down.
20378                     this.el.setXY(offset);
20379                     var xy = this.alignEl.getAnchorXY('tl', false);
20380                     xy[0]-=10;xy[1]+=5; // << fix me
20381                     this.arrowEl.setXY(xy);
20382                     return true;
20383                 }
20384                 // call self...
20385                 return this.updatePosition('right', false);
20386             
20387             case 'top':
20388                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20389                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20390                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20391                     //normal display... or moved up/down.
20392                     this.el.setXY(offset);
20393                     var xy = this.alignEl.getAnchorXY('t', false);
20394                     xy[1]-=10; // << fix me
20395                     this.arrowEl.setXY(xy);
20396                     return true;
20397                 }
20398                 // fall through
20399                return this.updatePosition('bottom', false);
20400             
20401             case 'bottom':
20402                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20403                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20404                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20405                     //normal display... or moved up/down.
20406                     this.el.setXY(offset);
20407                     var xy = this.alignEl.getAnchorXY('b', false);
20408                      xy[1]+=2; // << fix me
20409                     this.arrowEl.setXY(xy);
20410                     return true;
20411                 }
20412                 // fall through
20413                 return this.updatePosition('top', false);
20414                 
20415             
20416         }
20417         
20418         
20419         return false;
20420     },
20421     
20422     hide : function()
20423     {
20424         this.el.setXY([0,0]);
20425         this.el.removeClass('in');
20426         this.el.hide();
20427         this.hoverState = null;
20428         this.maskEl.hide(); // always..
20429         this.fireEvent('hide', this);
20430     }
20431     
20432 });
20433
20434
20435 Roo.apply(Roo.bootstrap.Popover, {
20436
20437     alignment : {
20438         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20439         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20440         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20441         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20442     },
20443     
20444     zIndex : 20001,
20445
20446     clickHander : false,
20447     
20448     
20449
20450     onMouseDown : function(e)
20451     {
20452         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20453             /// what is nothing is showing..
20454             this.hideAll();
20455         }
20456          
20457     },
20458     
20459     
20460     popups : [],
20461     
20462     register : function(popup)
20463     {
20464         if (!Roo.bootstrap.Popover.clickHandler) {
20465             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20466         }
20467         // hide other popups.
20468         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20469         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20470         this.hideAll(); //<< why?
20471         //this.popups.push(popup);
20472     },
20473     hideAll : function()
20474     {
20475         this.popups.forEach(function(p) {
20476             p.hide();
20477         });
20478     },
20479     onShow : function() {
20480         Roo.bootstrap.Popover.popups.push(this);
20481     },
20482     onHide : function() {
20483         Roo.bootstrap.Popover.popups.remove(this);
20484     } 
20485
20486 });/*
20487  * - LGPL
20488  *
20489  * Card header - holder for the card header elements.
20490  * 
20491  */
20492
20493 /**
20494  * @class Roo.bootstrap.PopoverNav
20495  * @extends Roo.bootstrap.NavGroup
20496  * Bootstrap Popover header navigation class
20497  * @constructor
20498  * Create a new Popover Header Navigation 
20499  * @param {Object} config The config object
20500  */
20501
20502 Roo.bootstrap.PopoverNav = function(config){
20503     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20504 };
20505
20506 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20507     
20508     
20509     container_method : 'getPopoverHeader' 
20510     
20511      
20512     
20513     
20514    
20515 });
20516
20517  
20518
20519  /*
20520  * - LGPL
20521  *
20522  * Progress
20523  * 
20524  */
20525
20526 /**
20527  * @class Roo.bootstrap.Progress
20528  * @extends Roo.bootstrap.Component
20529  * Bootstrap Progress class
20530  * @cfg {Boolean} striped striped of the progress bar
20531  * @cfg {Boolean} active animated of the progress bar
20532  * 
20533  * 
20534  * @constructor
20535  * Create a new Progress
20536  * @param {Object} config The config object
20537  */
20538
20539 Roo.bootstrap.Progress = function(config){
20540     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20541 };
20542
20543 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20544     
20545     striped : false,
20546     active: false,
20547     
20548     getAutoCreate : function(){
20549         var cfg = {
20550             tag: 'div',
20551             cls: 'progress'
20552         };
20553         
20554         
20555         if(this.striped){
20556             cfg.cls += ' progress-striped';
20557         }
20558       
20559         if(this.active){
20560             cfg.cls += ' active';
20561         }
20562         
20563         
20564         return cfg;
20565     }
20566    
20567 });
20568
20569  
20570
20571  /*
20572  * - LGPL
20573  *
20574  * ProgressBar
20575  * 
20576  */
20577
20578 /**
20579  * @class Roo.bootstrap.ProgressBar
20580  * @extends Roo.bootstrap.Component
20581  * Bootstrap ProgressBar class
20582  * @cfg {Number} aria_valuenow aria-value now
20583  * @cfg {Number} aria_valuemin aria-value min
20584  * @cfg {Number} aria_valuemax aria-value max
20585  * @cfg {String} label label for the progress bar
20586  * @cfg {String} panel (success | info | warning | danger )
20587  * @cfg {String} role role of the progress bar
20588  * @cfg {String} sr_only text
20589  * 
20590  * 
20591  * @constructor
20592  * Create a new ProgressBar
20593  * @param {Object} config The config object
20594  */
20595
20596 Roo.bootstrap.ProgressBar = function(config){
20597     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20598 };
20599
20600 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20601     
20602     aria_valuenow : 0,
20603     aria_valuemin : 0,
20604     aria_valuemax : 100,
20605     label : false,
20606     panel : false,
20607     role : false,
20608     sr_only: false,
20609     
20610     getAutoCreate : function()
20611     {
20612         
20613         var cfg = {
20614             tag: 'div',
20615             cls: 'progress-bar',
20616             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20617         };
20618         
20619         if(this.sr_only){
20620             cfg.cn = {
20621                 tag: 'span',
20622                 cls: 'sr-only',
20623                 html: this.sr_only
20624             }
20625         }
20626         
20627         if(this.role){
20628             cfg.role = this.role;
20629         }
20630         
20631         if(this.aria_valuenow){
20632             cfg['aria-valuenow'] = this.aria_valuenow;
20633         }
20634         
20635         if(this.aria_valuemin){
20636             cfg['aria-valuemin'] = this.aria_valuemin;
20637         }
20638         
20639         if(this.aria_valuemax){
20640             cfg['aria-valuemax'] = this.aria_valuemax;
20641         }
20642         
20643         if(this.label && !this.sr_only){
20644             cfg.html = this.label;
20645         }
20646         
20647         if(this.panel){
20648             cfg.cls += ' progress-bar-' + this.panel;
20649         }
20650         
20651         return cfg;
20652     },
20653     
20654     update : function(aria_valuenow)
20655     {
20656         this.aria_valuenow = aria_valuenow;
20657         
20658         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20659     }
20660    
20661 });
20662
20663  
20664
20665  /*
20666  * - LGPL
20667  *
20668  * column
20669  * 
20670  */
20671
20672 /**
20673  * @class Roo.bootstrap.TabGroup
20674  * @extends Roo.bootstrap.Column
20675  * Bootstrap Column class
20676  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20677  * @cfg {Boolean} carousel true to make the group behave like a carousel
20678  * @cfg {Boolean} bullets show bullets for the panels
20679  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20680  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20681  * @cfg {Boolean} showarrow (true|false) show arrow default true
20682  * 
20683  * @constructor
20684  * Create a new TabGroup
20685  * @param {Object} config The config object
20686  */
20687
20688 Roo.bootstrap.TabGroup = function(config){
20689     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20690     if (!this.navId) {
20691         this.navId = Roo.id();
20692     }
20693     this.tabs = [];
20694     Roo.bootstrap.TabGroup.register(this);
20695     
20696 };
20697
20698 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20699     
20700     carousel : false,
20701     transition : false,
20702     bullets : 0,
20703     timer : 0,
20704     autoslide : false,
20705     slideFn : false,
20706     slideOnTouch : false,
20707     showarrow : true,
20708     
20709     getAutoCreate : function()
20710     {
20711         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20712         
20713         cfg.cls += ' tab-content';
20714         
20715         if (this.carousel) {
20716             cfg.cls += ' carousel slide';
20717             
20718             cfg.cn = [{
20719                cls : 'carousel-inner',
20720                cn : []
20721             }];
20722         
20723             if(this.bullets  && !Roo.isTouch){
20724                 
20725                 var bullets = {
20726                     cls : 'carousel-bullets',
20727                     cn : []
20728                 };
20729                
20730                 if(this.bullets_cls){
20731                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20732                 }
20733                 
20734                 bullets.cn.push({
20735                     cls : 'clear'
20736                 });
20737                 
20738                 cfg.cn[0].cn.push(bullets);
20739             }
20740             
20741             if(this.showarrow){
20742                 cfg.cn[0].cn.push({
20743                     tag : 'div',
20744                     class : 'carousel-arrow',
20745                     cn : [
20746                         {
20747                             tag : 'div',
20748                             class : 'carousel-prev',
20749                             cn : [
20750                                 {
20751                                     tag : 'i',
20752                                     class : 'fa fa-chevron-left'
20753                                 }
20754                             ]
20755                         },
20756                         {
20757                             tag : 'div',
20758                             class : 'carousel-next',
20759                             cn : [
20760                                 {
20761                                     tag : 'i',
20762                                     class : 'fa fa-chevron-right'
20763                                 }
20764                             ]
20765                         }
20766                     ]
20767                 });
20768             }
20769             
20770         }
20771         
20772         return cfg;
20773     },
20774     
20775     initEvents:  function()
20776     {
20777 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20778 //            this.el.on("touchstart", this.onTouchStart, this);
20779 //        }
20780         
20781         if(this.autoslide){
20782             var _this = this;
20783             
20784             this.slideFn = window.setInterval(function() {
20785                 _this.showPanelNext();
20786             }, this.timer);
20787         }
20788         
20789         if(this.showarrow){
20790             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20791             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20792         }
20793         
20794         
20795     },
20796     
20797 //    onTouchStart : function(e, el, o)
20798 //    {
20799 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20800 //            return;
20801 //        }
20802 //        
20803 //        this.showPanelNext();
20804 //    },
20805     
20806     
20807     getChildContainer : function()
20808     {
20809         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20810     },
20811     
20812     /**
20813     * register a Navigation item
20814     * @param {Roo.bootstrap.NavItem} the navitem to add
20815     */
20816     register : function(item)
20817     {
20818         this.tabs.push( item);
20819         item.navId = this.navId; // not really needed..
20820         this.addBullet();
20821     
20822     },
20823     
20824     getActivePanel : function()
20825     {
20826         var r = false;
20827         Roo.each(this.tabs, function(t) {
20828             if (t.active) {
20829                 r = t;
20830                 return false;
20831             }
20832             return null;
20833         });
20834         return r;
20835         
20836     },
20837     getPanelByName : function(n)
20838     {
20839         var r = false;
20840         Roo.each(this.tabs, function(t) {
20841             if (t.tabId == n) {
20842                 r = t;
20843                 return false;
20844             }
20845             return null;
20846         });
20847         return r;
20848     },
20849     indexOfPanel : function(p)
20850     {
20851         var r = false;
20852         Roo.each(this.tabs, function(t,i) {
20853             if (t.tabId == p.tabId) {
20854                 r = i;
20855                 return false;
20856             }
20857             return null;
20858         });
20859         return r;
20860     },
20861     /**
20862      * show a specific panel
20863      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20864      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20865      */
20866     showPanel : function (pan)
20867     {
20868         if(this.transition || typeof(pan) == 'undefined'){
20869             Roo.log("waiting for the transitionend");
20870             return false;
20871         }
20872         
20873         if (typeof(pan) == 'number') {
20874             pan = this.tabs[pan];
20875         }
20876         
20877         if (typeof(pan) == 'string') {
20878             pan = this.getPanelByName(pan);
20879         }
20880         
20881         var cur = this.getActivePanel();
20882         
20883         if(!pan || !cur){
20884             Roo.log('pan or acitve pan is undefined');
20885             return false;
20886         }
20887         
20888         if (pan.tabId == this.getActivePanel().tabId) {
20889             return true;
20890         }
20891         
20892         if (false === cur.fireEvent('beforedeactivate')) {
20893             return false;
20894         }
20895         
20896         if(this.bullets > 0 && !Roo.isTouch){
20897             this.setActiveBullet(this.indexOfPanel(pan));
20898         }
20899         
20900         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20901             
20902             //class="carousel-item carousel-item-next carousel-item-left"
20903             
20904             this.transition = true;
20905             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20906             var lr = dir == 'next' ? 'left' : 'right';
20907             pan.el.addClass(dir); // or prev
20908             pan.el.addClass('carousel-item-' + dir); // or prev
20909             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20910             cur.el.addClass(lr); // or right
20911             pan.el.addClass(lr);
20912             cur.el.addClass('carousel-item-' +lr); // or right
20913             pan.el.addClass('carousel-item-' +lr);
20914             
20915             
20916             var _this = this;
20917             cur.el.on('transitionend', function() {
20918                 Roo.log("trans end?");
20919                 
20920                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20921                 pan.setActive(true);
20922                 
20923                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20924                 cur.setActive(false);
20925                 
20926                 _this.transition = false;
20927                 
20928             }, this, { single:  true } );
20929             
20930             return true;
20931         }
20932         
20933         cur.setActive(false);
20934         pan.setActive(true);
20935         
20936         return true;
20937         
20938     },
20939     showPanelNext : function()
20940     {
20941         var i = this.indexOfPanel(this.getActivePanel());
20942         
20943         if (i >= this.tabs.length - 1 && !this.autoslide) {
20944             return;
20945         }
20946         
20947         if (i >= this.tabs.length - 1 && this.autoslide) {
20948             i = -1;
20949         }
20950         
20951         this.showPanel(this.tabs[i+1]);
20952     },
20953     
20954     showPanelPrev : function()
20955     {
20956         var i = this.indexOfPanel(this.getActivePanel());
20957         
20958         if (i  < 1 && !this.autoslide) {
20959             return;
20960         }
20961         
20962         if (i < 1 && this.autoslide) {
20963             i = this.tabs.length;
20964         }
20965         
20966         this.showPanel(this.tabs[i-1]);
20967     },
20968     
20969     
20970     addBullet: function()
20971     {
20972         if(!this.bullets || Roo.isTouch){
20973             return;
20974         }
20975         var ctr = this.el.select('.carousel-bullets',true).first();
20976         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20977         var bullet = ctr.createChild({
20978             cls : 'bullet bullet-' + i
20979         },ctr.dom.lastChild);
20980         
20981         
20982         var _this = this;
20983         
20984         bullet.on('click', (function(e, el, o, ii, t){
20985
20986             e.preventDefault();
20987
20988             this.showPanel(ii);
20989
20990             if(this.autoslide && this.slideFn){
20991                 clearInterval(this.slideFn);
20992                 this.slideFn = window.setInterval(function() {
20993                     _this.showPanelNext();
20994                 }, this.timer);
20995             }
20996
20997         }).createDelegate(this, [i, bullet], true));
20998                 
20999         
21000     },
21001      
21002     setActiveBullet : function(i)
21003     {
21004         if(Roo.isTouch){
21005             return;
21006         }
21007         
21008         Roo.each(this.el.select('.bullet', true).elements, function(el){
21009             el.removeClass('selected');
21010         });
21011
21012         var bullet = this.el.select('.bullet-' + i, true).first();
21013         
21014         if(!bullet){
21015             return;
21016         }
21017         
21018         bullet.addClass('selected');
21019     }
21020     
21021     
21022   
21023 });
21024
21025  
21026
21027  
21028  
21029 Roo.apply(Roo.bootstrap.TabGroup, {
21030     
21031     groups: {},
21032      /**
21033     * register a Navigation Group
21034     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21035     */
21036     register : function(navgrp)
21037     {
21038         this.groups[navgrp.navId] = navgrp;
21039         
21040     },
21041     /**
21042     * fetch a Navigation Group based on the navigation ID
21043     * if one does not exist , it will get created.
21044     * @param {string} the navgroup to add
21045     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21046     */
21047     get: function(navId) {
21048         if (typeof(this.groups[navId]) == 'undefined') {
21049             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21050         }
21051         return this.groups[navId] ;
21052     }
21053     
21054     
21055     
21056 });
21057
21058  /*
21059  * - LGPL
21060  *
21061  * TabPanel
21062  * 
21063  */
21064
21065 /**
21066  * @class Roo.bootstrap.TabPanel
21067  * @extends Roo.bootstrap.Component
21068  * Bootstrap TabPanel class
21069  * @cfg {Boolean} active panel active
21070  * @cfg {String} html panel content
21071  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21072  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21073  * @cfg {String} href click to link..
21074  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21075  * 
21076  * 
21077  * @constructor
21078  * Create a new TabPanel
21079  * @param {Object} config The config object
21080  */
21081
21082 Roo.bootstrap.TabPanel = function(config){
21083     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21084     this.addEvents({
21085         /**
21086              * @event changed
21087              * Fires when the active status changes
21088              * @param {Roo.bootstrap.TabPanel} this
21089              * @param {Boolean} state the new state
21090             
21091          */
21092         'changed': true,
21093         /**
21094              * @event beforedeactivate
21095              * Fires before a tab is de-activated - can be used to do validation on a form.
21096              * @param {Roo.bootstrap.TabPanel} this
21097              * @return {Boolean} false if there is an error
21098             
21099          */
21100         'beforedeactivate': true
21101      });
21102     
21103     this.tabId = this.tabId || Roo.id();
21104   
21105 };
21106
21107 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21108     
21109     active: false,
21110     html: false,
21111     tabId: false,
21112     navId : false,
21113     href : '',
21114     touchSlide : false,
21115     getAutoCreate : function(){
21116         
21117         
21118         var cfg = {
21119             tag: 'div',
21120             // item is needed for carousel - not sure if it has any effect otherwise
21121             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21122             html: this.html || ''
21123         };
21124         
21125         if(this.active){
21126             cfg.cls += ' active';
21127         }
21128         
21129         if(this.tabId){
21130             cfg.tabId = this.tabId;
21131         }
21132         
21133         
21134         
21135         return cfg;
21136     },
21137     
21138     initEvents:  function()
21139     {
21140         var p = this.parent();
21141         
21142         this.navId = this.navId || p.navId;
21143         
21144         if (typeof(this.navId) != 'undefined') {
21145             // not really needed.. but just in case.. parent should be a NavGroup.
21146             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21147             
21148             tg.register(this);
21149             
21150             var i = tg.tabs.length - 1;
21151             
21152             if(this.active && tg.bullets > 0 && i < tg.bullets){
21153                 tg.setActiveBullet(i);
21154             }
21155         }
21156         
21157         this.el.on('click', this.onClick, this);
21158         
21159         if(Roo.isTouch && this.touchSlide){
21160             this.el.on("touchstart", this.onTouchStart, this);
21161             this.el.on("touchmove", this.onTouchMove, this);
21162             this.el.on("touchend", this.onTouchEnd, this);
21163         }
21164         
21165     },
21166     
21167     onRender : function(ct, position)
21168     {
21169         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21170     },
21171     
21172     setActive : function(state)
21173     {
21174         Roo.log("panel - set active " + this.tabId + "=" + state);
21175         
21176         this.active = state;
21177         if (!state) {
21178             this.el.removeClass('active');
21179             
21180         } else  if (!this.el.hasClass('active')) {
21181             this.el.addClass('active');
21182         }
21183         
21184         this.fireEvent('changed', this, state);
21185     },
21186     
21187     onClick : function(e)
21188     {
21189         e.preventDefault();
21190         
21191         if(!this.href.length){
21192             return;
21193         }
21194         
21195         window.location.href = this.href;
21196     },
21197     
21198     startX : 0,
21199     startY : 0,
21200     endX : 0,
21201     endY : 0,
21202     swiping : false,
21203     
21204     onTouchStart : function(e)
21205     {
21206         this.swiping = false;
21207         
21208         this.startX = e.browserEvent.touches[0].clientX;
21209         this.startY = e.browserEvent.touches[0].clientY;
21210     },
21211     
21212     onTouchMove : function(e)
21213     {
21214         this.swiping = true;
21215         
21216         this.endX = e.browserEvent.touches[0].clientX;
21217         this.endY = e.browserEvent.touches[0].clientY;
21218     },
21219     
21220     onTouchEnd : function(e)
21221     {
21222         if(!this.swiping){
21223             this.onClick(e);
21224             return;
21225         }
21226         
21227         var tabGroup = this.parent();
21228         
21229         if(this.endX > this.startX){ // swiping right
21230             tabGroup.showPanelPrev();
21231             return;
21232         }
21233         
21234         if(this.startX > this.endX){ // swiping left
21235             tabGroup.showPanelNext();
21236             return;
21237         }
21238     }
21239     
21240     
21241 });
21242  
21243
21244  
21245
21246  /*
21247  * - LGPL
21248  *
21249  * DateField
21250  * 
21251  */
21252
21253 /**
21254  * @class Roo.bootstrap.DateField
21255  * @extends Roo.bootstrap.Input
21256  * Bootstrap DateField class
21257  * @cfg {Number} weekStart default 0
21258  * @cfg {String} viewMode default empty, (months|years)
21259  * @cfg {String} minViewMode default empty, (months|years)
21260  * @cfg {Number} startDate default -Infinity
21261  * @cfg {Number} endDate default Infinity
21262  * @cfg {Boolean} todayHighlight default false
21263  * @cfg {Boolean} todayBtn default false
21264  * @cfg {Boolean} calendarWeeks default false
21265  * @cfg {Object} daysOfWeekDisabled default empty
21266  * @cfg {Boolean} singleMode default false (true | false)
21267  * 
21268  * @cfg {Boolean} keyboardNavigation default true
21269  * @cfg {String} language default en
21270  * 
21271  * @constructor
21272  * Create a new DateField
21273  * @param {Object} config The config object
21274  */
21275
21276 Roo.bootstrap.DateField = function(config){
21277     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21278      this.addEvents({
21279             /**
21280              * @event show
21281              * Fires when this field show.
21282              * @param {Roo.bootstrap.DateField} this
21283              * @param {Mixed} date The date value
21284              */
21285             show : true,
21286             /**
21287              * @event show
21288              * Fires when this field hide.
21289              * @param {Roo.bootstrap.DateField} this
21290              * @param {Mixed} date The date value
21291              */
21292             hide : true,
21293             /**
21294              * @event select
21295              * Fires when select a date.
21296              * @param {Roo.bootstrap.DateField} this
21297              * @param {Mixed} date The date value
21298              */
21299             select : true,
21300             /**
21301              * @event beforeselect
21302              * Fires when before select a date.
21303              * @param {Roo.bootstrap.DateField} this
21304              * @param {Mixed} date The date value
21305              */
21306             beforeselect : true
21307         });
21308 };
21309
21310 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21311     
21312     /**
21313      * @cfg {String} format
21314      * The default date format string which can be overriden for localization support.  The format must be
21315      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21316      */
21317     format : "m/d/y",
21318     /**
21319      * @cfg {String} altFormats
21320      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21321      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21322      */
21323     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21324     
21325     weekStart : 0,
21326     
21327     viewMode : '',
21328     
21329     minViewMode : '',
21330     
21331     todayHighlight : false,
21332     
21333     todayBtn: false,
21334     
21335     language: 'en',
21336     
21337     keyboardNavigation: true,
21338     
21339     calendarWeeks: false,
21340     
21341     startDate: -Infinity,
21342     
21343     endDate: Infinity,
21344     
21345     daysOfWeekDisabled: [],
21346     
21347     _events: [],
21348     
21349     singleMode : false,
21350     
21351     UTCDate: function()
21352     {
21353         return new Date(Date.UTC.apply(Date, arguments));
21354     },
21355     
21356     UTCToday: function()
21357     {
21358         var today = new Date();
21359         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21360     },
21361     
21362     getDate: function() {
21363             var d = this.getUTCDate();
21364             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21365     },
21366     
21367     getUTCDate: function() {
21368             return this.date;
21369     },
21370     
21371     setDate: function(d) {
21372             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21373     },
21374     
21375     setUTCDate: function(d) {
21376             this.date = d;
21377             this.setValue(this.formatDate(this.date));
21378     },
21379         
21380     onRender: function(ct, position)
21381     {
21382         
21383         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21384         
21385         this.language = this.language || 'en';
21386         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21387         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21388         
21389         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21390         this.format = this.format || 'm/d/y';
21391         this.isInline = false;
21392         this.isInput = true;
21393         this.component = this.el.select('.add-on', true).first() || false;
21394         this.component = (this.component && this.component.length === 0) ? false : this.component;
21395         this.hasInput = this.component && this.inputEl().length;
21396         
21397         if (typeof(this.minViewMode === 'string')) {
21398             switch (this.minViewMode) {
21399                 case 'months':
21400                     this.minViewMode = 1;
21401                     break;
21402                 case 'years':
21403                     this.minViewMode = 2;
21404                     break;
21405                 default:
21406                     this.minViewMode = 0;
21407                     break;
21408             }
21409         }
21410         
21411         if (typeof(this.viewMode === 'string')) {
21412             switch (this.viewMode) {
21413                 case 'months':
21414                     this.viewMode = 1;
21415                     break;
21416                 case 'years':
21417                     this.viewMode = 2;
21418                     break;
21419                 default:
21420                     this.viewMode = 0;
21421                     break;
21422             }
21423         }
21424                 
21425         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21426         
21427 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21428         
21429         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21430         
21431         this.picker().on('mousedown', this.onMousedown, this);
21432         this.picker().on('click', this.onClick, this);
21433         
21434         this.picker().addClass('datepicker-dropdown');
21435         
21436         this.startViewMode = this.viewMode;
21437         
21438         if(this.singleMode){
21439             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21440                 v.setVisibilityMode(Roo.Element.DISPLAY);
21441                 v.hide();
21442             });
21443             
21444             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21445                 v.setStyle('width', '189px');
21446             });
21447         }
21448         
21449         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21450             if(!this.calendarWeeks){
21451                 v.remove();
21452                 return;
21453             }
21454             
21455             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21456             v.attr('colspan', function(i, val){
21457                 return parseInt(val) + 1;
21458             });
21459         });
21460                         
21461         
21462         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21463         
21464         this.setStartDate(this.startDate);
21465         this.setEndDate(this.endDate);
21466         
21467         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21468         
21469         this.fillDow();
21470         this.fillMonths();
21471         this.update();
21472         this.showMode();
21473         
21474         if(this.isInline) {
21475             this.showPopup();
21476         }
21477     },
21478     
21479     picker : function()
21480     {
21481         return this.pickerEl;
21482 //        return this.el.select('.datepicker', true).first();
21483     },
21484     
21485     fillDow: function()
21486     {
21487         var dowCnt = this.weekStart;
21488         
21489         var dow = {
21490             tag: 'tr',
21491             cn: [
21492                 
21493             ]
21494         };
21495         
21496         if(this.calendarWeeks){
21497             dow.cn.push({
21498                 tag: 'th',
21499                 cls: 'cw',
21500                 html: '&nbsp;'
21501             })
21502         }
21503         
21504         while (dowCnt < this.weekStart + 7) {
21505             dow.cn.push({
21506                 tag: 'th',
21507                 cls: 'dow',
21508                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21509             });
21510         }
21511         
21512         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21513     },
21514     
21515     fillMonths: function()
21516     {    
21517         var i = 0;
21518         var months = this.picker().select('>.datepicker-months td', true).first();
21519         
21520         months.dom.innerHTML = '';
21521         
21522         while (i < 12) {
21523             var month = {
21524                 tag: 'span',
21525                 cls: 'month',
21526                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21527             };
21528             
21529             months.createChild(month);
21530         }
21531         
21532     },
21533     
21534     update: function()
21535     {
21536         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;
21537         
21538         if (this.date < this.startDate) {
21539             this.viewDate = new Date(this.startDate);
21540         } else if (this.date > this.endDate) {
21541             this.viewDate = new Date(this.endDate);
21542         } else {
21543             this.viewDate = new Date(this.date);
21544         }
21545         
21546         this.fill();
21547     },
21548     
21549     fill: function() 
21550     {
21551         var d = new Date(this.viewDate),
21552                 year = d.getUTCFullYear(),
21553                 month = d.getUTCMonth(),
21554                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21555                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21556                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21557                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21558                 currentDate = this.date && this.date.valueOf(),
21559                 today = this.UTCToday();
21560         
21561         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21562         
21563 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21564         
21565 //        this.picker.select('>tfoot th.today').
21566 //                                              .text(dates[this.language].today)
21567 //                                              .toggle(this.todayBtn !== false);
21568     
21569         this.updateNavArrows();
21570         this.fillMonths();
21571                                                 
21572         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21573         
21574         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21575          
21576         prevMonth.setUTCDate(day);
21577         
21578         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21579         
21580         var nextMonth = new Date(prevMonth);
21581         
21582         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21583         
21584         nextMonth = nextMonth.valueOf();
21585         
21586         var fillMonths = false;
21587         
21588         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21589         
21590         while(prevMonth.valueOf() <= nextMonth) {
21591             var clsName = '';
21592             
21593             if (prevMonth.getUTCDay() === this.weekStart) {
21594                 if(fillMonths){
21595                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21596                 }
21597                     
21598                 fillMonths = {
21599                     tag: 'tr',
21600                     cn: []
21601                 };
21602                 
21603                 if(this.calendarWeeks){
21604                     // ISO 8601: First week contains first thursday.
21605                     // ISO also states week starts on Monday, but we can be more abstract here.
21606                     var
21607                     // Start of current week: based on weekstart/current date
21608                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21609                     // Thursday of this week
21610                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21611                     // First Thursday of year, year from thursday
21612                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21613                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21614                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21615                     
21616                     fillMonths.cn.push({
21617                         tag: 'td',
21618                         cls: 'cw',
21619                         html: calWeek
21620                     });
21621                 }
21622             }
21623             
21624             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21625                 clsName += ' old';
21626             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21627                 clsName += ' new';
21628             }
21629             if (this.todayHighlight &&
21630                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21631                 prevMonth.getUTCMonth() == today.getMonth() &&
21632                 prevMonth.getUTCDate() == today.getDate()) {
21633                 clsName += ' today';
21634             }
21635             
21636             if (currentDate && prevMonth.valueOf() === currentDate) {
21637                 clsName += ' active';
21638             }
21639             
21640             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21641                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21642                     clsName += ' disabled';
21643             }
21644             
21645             fillMonths.cn.push({
21646                 tag: 'td',
21647                 cls: 'day ' + clsName,
21648                 html: prevMonth.getDate()
21649             });
21650             
21651             prevMonth.setDate(prevMonth.getDate()+1);
21652         }
21653           
21654         var currentYear = this.date && this.date.getUTCFullYear();
21655         var currentMonth = this.date && this.date.getUTCMonth();
21656         
21657         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21658         
21659         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21660             v.removeClass('active');
21661             
21662             if(currentYear === year && k === currentMonth){
21663                 v.addClass('active');
21664             }
21665             
21666             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21667                 v.addClass('disabled');
21668             }
21669             
21670         });
21671         
21672         
21673         year = parseInt(year/10, 10) * 10;
21674         
21675         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21676         
21677         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21678         
21679         year -= 1;
21680         for (var i = -1; i < 11; i++) {
21681             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21682                 tag: 'span',
21683                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21684                 html: year
21685             });
21686             
21687             year += 1;
21688         }
21689     },
21690     
21691     showMode: function(dir) 
21692     {
21693         if (dir) {
21694             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21695         }
21696         
21697         Roo.each(this.picker().select('>div',true).elements, function(v){
21698             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21699             v.hide();
21700         });
21701         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21702     },
21703     
21704     place: function()
21705     {
21706         if(this.isInline) {
21707             return;
21708         }
21709         
21710         this.picker().removeClass(['bottom', 'top']);
21711         
21712         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21713             /*
21714              * place to the top of element!
21715              *
21716              */
21717             
21718             this.picker().addClass('top');
21719             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21720             
21721             return;
21722         }
21723         
21724         this.picker().addClass('bottom');
21725         
21726         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21727     },
21728     
21729     parseDate : function(value)
21730     {
21731         if(!value || value instanceof Date){
21732             return value;
21733         }
21734         var v = Date.parseDate(value, this.format);
21735         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21736             v = Date.parseDate(value, 'Y-m-d');
21737         }
21738         if(!v && this.altFormats){
21739             if(!this.altFormatsArray){
21740                 this.altFormatsArray = this.altFormats.split("|");
21741             }
21742             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21743                 v = Date.parseDate(value, this.altFormatsArray[i]);
21744             }
21745         }
21746         return v;
21747     },
21748     
21749     formatDate : function(date, fmt)
21750     {   
21751         return (!date || !(date instanceof Date)) ?
21752         date : date.dateFormat(fmt || this.format);
21753     },
21754     
21755     onFocus : function()
21756     {
21757         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21758         this.showPopup();
21759     },
21760     
21761     onBlur : function()
21762     {
21763         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21764         
21765         var d = this.inputEl().getValue();
21766         
21767         this.setValue(d);
21768                 
21769         this.hidePopup();
21770     },
21771     
21772     showPopup : function()
21773     {
21774         this.picker().show();
21775         this.update();
21776         this.place();
21777         
21778         this.fireEvent('showpopup', this, this.date);
21779     },
21780     
21781     hidePopup : function()
21782     {
21783         if(this.isInline) {
21784             return;
21785         }
21786         this.picker().hide();
21787         this.viewMode = this.startViewMode;
21788         this.showMode();
21789         
21790         this.fireEvent('hidepopup', this, this.date);
21791         
21792     },
21793     
21794     onMousedown: function(e)
21795     {
21796         e.stopPropagation();
21797         e.preventDefault();
21798     },
21799     
21800     keyup: function(e)
21801     {
21802         Roo.bootstrap.DateField.superclass.keyup.call(this);
21803         this.update();
21804     },
21805
21806     setValue: function(v)
21807     {
21808         if(this.fireEvent('beforeselect', this, v) !== false){
21809             var d = new Date(this.parseDate(v) ).clearTime();
21810         
21811             if(isNaN(d.getTime())){
21812                 this.date = this.viewDate = '';
21813                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21814                 return;
21815             }
21816
21817             v = this.formatDate(d);
21818
21819             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21820
21821             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21822
21823             this.update();
21824
21825             this.fireEvent('select', this, this.date);
21826         }
21827     },
21828     
21829     getValue: function()
21830     {
21831         return this.formatDate(this.date);
21832     },
21833     
21834     fireKey: function(e)
21835     {
21836         if (!this.picker().isVisible()){
21837             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21838                 this.showPopup();
21839             }
21840             return;
21841         }
21842         
21843         var dateChanged = false,
21844         dir, day, month,
21845         newDate, newViewDate;
21846         
21847         switch(e.keyCode){
21848             case 27: // escape
21849                 this.hidePopup();
21850                 e.preventDefault();
21851                 break;
21852             case 37: // left
21853             case 39: // right
21854                 if (!this.keyboardNavigation) {
21855                     break;
21856                 }
21857                 dir = e.keyCode == 37 ? -1 : 1;
21858                 
21859                 if (e.ctrlKey){
21860                     newDate = this.moveYear(this.date, dir);
21861                     newViewDate = this.moveYear(this.viewDate, dir);
21862                 } else if (e.shiftKey){
21863                     newDate = this.moveMonth(this.date, dir);
21864                     newViewDate = this.moveMonth(this.viewDate, dir);
21865                 } else {
21866                     newDate = new Date(this.date);
21867                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21868                     newViewDate = new Date(this.viewDate);
21869                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21870                 }
21871                 if (this.dateWithinRange(newDate)){
21872                     this.date = newDate;
21873                     this.viewDate = newViewDate;
21874                     this.setValue(this.formatDate(this.date));
21875 //                    this.update();
21876                     e.preventDefault();
21877                     dateChanged = true;
21878                 }
21879                 break;
21880             case 38: // up
21881             case 40: // down
21882                 if (!this.keyboardNavigation) {
21883                     break;
21884                 }
21885                 dir = e.keyCode == 38 ? -1 : 1;
21886                 if (e.ctrlKey){
21887                     newDate = this.moveYear(this.date, dir);
21888                     newViewDate = this.moveYear(this.viewDate, dir);
21889                 } else if (e.shiftKey){
21890                     newDate = this.moveMonth(this.date, dir);
21891                     newViewDate = this.moveMonth(this.viewDate, dir);
21892                 } else {
21893                     newDate = new Date(this.date);
21894                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21895                     newViewDate = new Date(this.viewDate);
21896                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21897                 }
21898                 if (this.dateWithinRange(newDate)){
21899                     this.date = newDate;
21900                     this.viewDate = newViewDate;
21901                     this.setValue(this.formatDate(this.date));
21902 //                    this.update();
21903                     e.preventDefault();
21904                     dateChanged = true;
21905                 }
21906                 break;
21907             case 13: // enter
21908                 this.setValue(this.formatDate(this.date));
21909                 this.hidePopup();
21910                 e.preventDefault();
21911                 break;
21912             case 9: // tab
21913                 this.setValue(this.formatDate(this.date));
21914                 this.hidePopup();
21915                 break;
21916             case 16: // shift
21917             case 17: // ctrl
21918             case 18: // alt
21919                 break;
21920             default :
21921                 this.hidePopup();
21922                 
21923         }
21924     },
21925     
21926     
21927     onClick: function(e) 
21928     {
21929         e.stopPropagation();
21930         e.preventDefault();
21931         
21932         var target = e.getTarget();
21933         
21934         if(target.nodeName.toLowerCase() === 'i'){
21935             target = Roo.get(target).dom.parentNode;
21936         }
21937         
21938         var nodeName = target.nodeName;
21939         var className = target.className;
21940         var html = target.innerHTML;
21941         //Roo.log(nodeName);
21942         
21943         switch(nodeName.toLowerCase()) {
21944             case 'th':
21945                 switch(className) {
21946                     case 'switch':
21947                         this.showMode(1);
21948                         break;
21949                     case 'prev':
21950                     case 'next':
21951                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21952                         switch(this.viewMode){
21953                                 case 0:
21954                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21955                                         break;
21956                                 case 1:
21957                                 case 2:
21958                                         this.viewDate = this.moveYear(this.viewDate, dir);
21959                                         break;
21960                         }
21961                         this.fill();
21962                         break;
21963                     case 'today':
21964                         var date = new Date();
21965                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21966 //                        this.fill()
21967                         this.setValue(this.formatDate(this.date));
21968                         
21969                         this.hidePopup();
21970                         break;
21971                 }
21972                 break;
21973             case 'span':
21974                 if (className.indexOf('disabled') < 0) {
21975                 if (!this.viewDate) {
21976                     this.viewDate = new Date();
21977                 }
21978                 this.viewDate.setUTCDate(1);
21979                     if (className.indexOf('month') > -1) {
21980                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21981                     } else {
21982                         var year = parseInt(html, 10) || 0;
21983                         this.viewDate.setUTCFullYear(year);
21984                         
21985                     }
21986                     
21987                     if(this.singleMode){
21988                         this.setValue(this.formatDate(this.viewDate));
21989                         this.hidePopup();
21990                         return;
21991                     }
21992                     
21993                     this.showMode(-1);
21994                     this.fill();
21995                 }
21996                 break;
21997                 
21998             case 'td':
21999                 //Roo.log(className);
22000                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22001                     var day = parseInt(html, 10) || 1;
22002                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22003                         month = (this.viewDate || new Date()).getUTCMonth();
22004
22005                     if (className.indexOf('old') > -1) {
22006                         if(month === 0 ){
22007                             month = 11;
22008                             year -= 1;
22009                         }else{
22010                             month -= 1;
22011                         }
22012                     } else if (className.indexOf('new') > -1) {
22013                         if (month == 11) {
22014                             month = 0;
22015                             year += 1;
22016                         } else {
22017                             month += 1;
22018                         }
22019                     }
22020                     //Roo.log([year,month,day]);
22021                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22022                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22023 //                    this.fill();
22024                     //Roo.log(this.formatDate(this.date));
22025                     this.setValue(this.formatDate(this.date));
22026                     this.hidePopup();
22027                 }
22028                 break;
22029         }
22030     },
22031     
22032     setStartDate: function(startDate)
22033     {
22034         this.startDate = startDate || -Infinity;
22035         if (this.startDate !== -Infinity) {
22036             this.startDate = this.parseDate(this.startDate);
22037         }
22038         this.update();
22039         this.updateNavArrows();
22040     },
22041
22042     setEndDate: function(endDate)
22043     {
22044         this.endDate = endDate || Infinity;
22045         if (this.endDate !== Infinity) {
22046             this.endDate = this.parseDate(this.endDate);
22047         }
22048         this.update();
22049         this.updateNavArrows();
22050     },
22051     
22052     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22053     {
22054         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22055         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22056             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22057         }
22058         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22059             return parseInt(d, 10);
22060         });
22061         this.update();
22062         this.updateNavArrows();
22063     },
22064     
22065     updateNavArrows: function() 
22066     {
22067         if(this.singleMode){
22068             return;
22069         }
22070         
22071         var d = new Date(this.viewDate),
22072         year = d.getUTCFullYear(),
22073         month = d.getUTCMonth();
22074         
22075         Roo.each(this.picker().select('.prev', true).elements, function(v){
22076             v.show();
22077             switch (this.viewMode) {
22078                 case 0:
22079
22080                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22081                         v.hide();
22082                     }
22083                     break;
22084                 case 1:
22085                 case 2:
22086                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22087                         v.hide();
22088                     }
22089                     break;
22090             }
22091         });
22092         
22093         Roo.each(this.picker().select('.next', true).elements, function(v){
22094             v.show();
22095             switch (this.viewMode) {
22096                 case 0:
22097
22098                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22099                         v.hide();
22100                     }
22101                     break;
22102                 case 1:
22103                 case 2:
22104                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22105                         v.hide();
22106                     }
22107                     break;
22108             }
22109         })
22110     },
22111     
22112     moveMonth: function(date, dir)
22113     {
22114         if (!dir) {
22115             return date;
22116         }
22117         var new_date = new Date(date.valueOf()),
22118         day = new_date.getUTCDate(),
22119         month = new_date.getUTCMonth(),
22120         mag = Math.abs(dir),
22121         new_month, test;
22122         dir = dir > 0 ? 1 : -1;
22123         if (mag == 1){
22124             test = dir == -1
22125             // If going back one month, make sure month is not current month
22126             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22127             ? function(){
22128                 return new_date.getUTCMonth() == month;
22129             }
22130             // If going forward one month, make sure month is as expected
22131             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22132             : function(){
22133                 return new_date.getUTCMonth() != new_month;
22134             };
22135             new_month = month + dir;
22136             new_date.setUTCMonth(new_month);
22137             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22138             if (new_month < 0 || new_month > 11) {
22139                 new_month = (new_month + 12) % 12;
22140             }
22141         } else {
22142             // For magnitudes >1, move one month at a time...
22143             for (var i=0; i<mag; i++) {
22144                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22145                 new_date = this.moveMonth(new_date, dir);
22146             }
22147             // ...then reset the day, keeping it in the new month
22148             new_month = new_date.getUTCMonth();
22149             new_date.setUTCDate(day);
22150             test = function(){
22151                 return new_month != new_date.getUTCMonth();
22152             };
22153         }
22154         // Common date-resetting loop -- if date is beyond end of month, make it
22155         // end of month
22156         while (test()){
22157             new_date.setUTCDate(--day);
22158             new_date.setUTCMonth(new_month);
22159         }
22160         return new_date;
22161     },
22162
22163     moveYear: function(date, dir)
22164     {
22165         return this.moveMonth(date, dir*12);
22166     },
22167
22168     dateWithinRange: function(date)
22169     {
22170         return date >= this.startDate && date <= this.endDate;
22171     },
22172
22173     
22174     remove: function() 
22175     {
22176         this.picker().remove();
22177     },
22178     
22179     validateValue : function(value)
22180     {
22181         if(this.getVisibilityEl().hasClass('hidden')){
22182             return true;
22183         }
22184         
22185         if(value.length < 1)  {
22186             if(this.allowBlank){
22187                 return true;
22188             }
22189             return false;
22190         }
22191         
22192         if(value.length < this.minLength){
22193             return false;
22194         }
22195         if(value.length > this.maxLength){
22196             return false;
22197         }
22198         if(this.vtype){
22199             var vt = Roo.form.VTypes;
22200             if(!vt[this.vtype](value, this)){
22201                 return false;
22202             }
22203         }
22204         if(typeof this.validator == "function"){
22205             var msg = this.validator(value);
22206             if(msg !== true){
22207                 return false;
22208             }
22209         }
22210         
22211         if(this.regex && !this.regex.test(value)){
22212             return false;
22213         }
22214         
22215         if(typeof(this.parseDate(value)) == 'undefined'){
22216             return false;
22217         }
22218         
22219         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22220             return false;
22221         }      
22222         
22223         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22224             return false;
22225         } 
22226         
22227         
22228         return true;
22229     },
22230     
22231     reset : function()
22232     {
22233         this.date = this.viewDate = '';
22234         
22235         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22236     }
22237    
22238 });
22239
22240 Roo.apply(Roo.bootstrap.DateField,  {
22241     
22242     head : {
22243         tag: 'thead',
22244         cn: [
22245         {
22246             tag: 'tr',
22247             cn: [
22248             {
22249                 tag: 'th',
22250                 cls: 'prev',
22251                 html: '<i class="fa fa-arrow-left"/>'
22252             },
22253             {
22254                 tag: 'th',
22255                 cls: 'switch',
22256                 colspan: '5'
22257             },
22258             {
22259                 tag: 'th',
22260                 cls: 'next',
22261                 html: '<i class="fa fa-arrow-right"/>'
22262             }
22263
22264             ]
22265         }
22266         ]
22267     },
22268     
22269     content : {
22270         tag: 'tbody',
22271         cn: [
22272         {
22273             tag: 'tr',
22274             cn: [
22275             {
22276                 tag: 'td',
22277                 colspan: '7'
22278             }
22279             ]
22280         }
22281         ]
22282     },
22283     
22284     footer : {
22285         tag: 'tfoot',
22286         cn: [
22287         {
22288             tag: 'tr',
22289             cn: [
22290             {
22291                 tag: 'th',
22292                 colspan: '7',
22293                 cls: 'today'
22294             }
22295                     
22296             ]
22297         }
22298         ]
22299     },
22300     
22301     dates:{
22302         en: {
22303             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22304             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22305             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22306             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22307             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22308             today: "Today"
22309         }
22310     },
22311     
22312     modes: [
22313     {
22314         clsName: 'days',
22315         navFnc: 'Month',
22316         navStep: 1
22317     },
22318     {
22319         clsName: 'months',
22320         navFnc: 'FullYear',
22321         navStep: 1
22322     },
22323     {
22324         clsName: 'years',
22325         navFnc: 'FullYear',
22326         navStep: 10
22327     }]
22328 });
22329
22330 Roo.apply(Roo.bootstrap.DateField,  {
22331   
22332     template : {
22333         tag: 'div',
22334         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22335         cn: [
22336         {
22337             tag: 'div',
22338             cls: 'datepicker-days',
22339             cn: [
22340             {
22341                 tag: 'table',
22342                 cls: 'table-condensed',
22343                 cn:[
22344                 Roo.bootstrap.DateField.head,
22345                 {
22346                     tag: 'tbody'
22347                 },
22348                 Roo.bootstrap.DateField.footer
22349                 ]
22350             }
22351             ]
22352         },
22353         {
22354             tag: 'div',
22355             cls: 'datepicker-months',
22356             cn: [
22357             {
22358                 tag: 'table',
22359                 cls: 'table-condensed',
22360                 cn:[
22361                 Roo.bootstrap.DateField.head,
22362                 Roo.bootstrap.DateField.content,
22363                 Roo.bootstrap.DateField.footer
22364                 ]
22365             }
22366             ]
22367         },
22368         {
22369             tag: 'div',
22370             cls: 'datepicker-years',
22371             cn: [
22372             {
22373                 tag: 'table',
22374                 cls: 'table-condensed',
22375                 cn:[
22376                 Roo.bootstrap.DateField.head,
22377                 Roo.bootstrap.DateField.content,
22378                 Roo.bootstrap.DateField.footer
22379                 ]
22380             }
22381             ]
22382         }
22383         ]
22384     }
22385 });
22386
22387  
22388
22389  /*
22390  * - LGPL
22391  *
22392  * TimeField
22393  * 
22394  */
22395
22396 /**
22397  * @class Roo.bootstrap.TimeField
22398  * @extends Roo.bootstrap.Input
22399  * Bootstrap DateField class
22400  * 
22401  * 
22402  * @constructor
22403  * Create a new TimeField
22404  * @param {Object} config The config object
22405  */
22406
22407 Roo.bootstrap.TimeField = function(config){
22408     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22409     this.addEvents({
22410             /**
22411              * @event show
22412              * Fires when this field show.
22413              * @param {Roo.bootstrap.DateField} thisthis
22414              * @param {Mixed} date The date value
22415              */
22416             show : true,
22417             /**
22418              * @event show
22419              * Fires when this field hide.
22420              * @param {Roo.bootstrap.DateField} this
22421              * @param {Mixed} date The date value
22422              */
22423             hide : true,
22424             /**
22425              * @event select
22426              * Fires when select a date.
22427              * @param {Roo.bootstrap.DateField} this
22428              * @param {Mixed} date The date value
22429              */
22430             select : true
22431         });
22432 };
22433
22434 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22435     
22436     /**
22437      * @cfg {String} format
22438      * The default time format string which can be overriden for localization support.  The format must be
22439      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22440      */
22441     format : "H:i",
22442
22443     getAutoCreate : function()
22444     {
22445         this.after = '<i class="fa far fa-clock"></i>';
22446         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22447         
22448          
22449     },
22450     onRender: function(ct, position)
22451     {
22452         
22453         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22454                 
22455         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22456         
22457         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22458         
22459         this.pop = this.picker().select('>.datepicker-time',true).first();
22460         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22461         
22462         this.picker().on('mousedown', this.onMousedown, this);
22463         this.picker().on('click', this.onClick, this);
22464         
22465         this.picker().addClass('datepicker-dropdown');
22466     
22467         this.fillTime();
22468         this.update();
22469             
22470         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22471         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22472         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22473         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22474         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22475         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22476
22477     },
22478     
22479     fireKey: function(e){
22480         if (!this.picker().isVisible()){
22481             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22482                 this.show();
22483             }
22484             return;
22485         }
22486
22487         e.preventDefault();
22488         
22489         switch(e.keyCode){
22490             case 27: // escape
22491                 this.hide();
22492                 break;
22493             case 37: // left
22494             case 39: // right
22495                 this.onTogglePeriod();
22496                 break;
22497             case 38: // up
22498                 this.onIncrementMinutes();
22499                 break;
22500             case 40: // down
22501                 this.onDecrementMinutes();
22502                 break;
22503             case 13: // enter
22504             case 9: // tab
22505                 this.setTime();
22506                 break;
22507         }
22508     },
22509     
22510     onClick: function(e) {
22511         e.stopPropagation();
22512         e.preventDefault();
22513     },
22514     
22515     picker : function()
22516     {
22517         return this.pickerEl;
22518     },
22519     
22520     fillTime: function()
22521     {    
22522         var time = this.pop.select('tbody', true).first();
22523         
22524         time.dom.innerHTML = '';
22525         
22526         time.createChild({
22527             tag: 'tr',
22528             cn: [
22529                 {
22530                     tag: 'td',
22531                     cn: [
22532                         {
22533                             tag: 'a',
22534                             href: '#',
22535                             cls: 'btn',
22536                             cn: [
22537                                 {
22538                                     tag: 'i',
22539                                     cls: 'hours-up fa fas fa-chevron-up'
22540                                 }
22541                             ]
22542                         } 
22543                     ]
22544                 },
22545                 {
22546                     tag: 'td',
22547                     cls: 'separator'
22548                 },
22549                 {
22550                     tag: 'td',
22551                     cn: [
22552                         {
22553                             tag: 'a',
22554                             href: '#',
22555                             cls: 'btn',
22556                             cn: [
22557                                 {
22558                                     tag: 'i',
22559                                     cls: 'minutes-up fa fas fa-chevron-up'
22560                                 }
22561                             ]
22562                         }
22563                     ]
22564                 },
22565                 {
22566                     tag: 'td',
22567                     cls: 'separator'
22568                 }
22569             ]
22570         });
22571         
22572         time.createChild({
22573             tag: 'tr',
22574             cn: [
22575                 {
22576                     tag: 'td',
22577                     cn: [
22578                         {
22579                             tag: 'span',
22580                             cls: 'timepicker-hour',
22581                             html: '00'
22582                         }  
22583                     ]
22584                 },
22585                 {
22586                     tag: 'td',
22587                     cls: 'separator',
22588                     html: ':'
22589                 },
22590                 {
22591                     tag: 'td',
22592                     cn: [
22593                         {
22594                             tag: 'span',
22595                             cls: 'timepicker-minute',
22596                             html: '00'
22597                         }  
22598                     ]
22599                 },
22600                 {
22601                     tag: 'td',
22602                     cls: 'separator'
22603                 },
22604                 {
22605                     tag: 'td',
22606                     cn: [
22607                         {
22608                             tag: 'button',
22609                             type: 'button',
22610                             cls: 'btn btn-primary period',
22611                             html: 'AM'
22612                             
22613                         }
22614                     ]
22615                 }
22616             ]
22617         });
22618         
22619         time.createChild({
22620             tag: 'tr',
22621             cn: [
22622                 {
22623                     tag: 'td',
22624                     cn: [
22625                         {
22626                             tag: 'a',
22627                             href: '#',
22628                             cls: 'btn',
22629                             cn: [
22630                                 {
22631                                     tag: 'span',
22632                                     cls: 'hours-down fa fas fa-chevron-down'
22633                                 }
22634                             ]
22635                         }
22636                     ]
22637                 },
22638                 {
22639                     tag: 'td',
22640                     cls: 'separator'
22641                 },
22642                 {
22643                     tag: 'td',
22644                     cn: [
22645                         {
22646                             tag: 'a',
22647                             href: '#',
22648                             cls: 'btn',
22649                             cn: [
22650                                 {
22651                                     tag: 'span',
22652                                     cls: 'minutes-down fa fas fa-chevron-down'
22653                                 }
22654                             ]
22655                         }
22656                     ]
22657                 },
22658                 {
22659                     tag: 'td',
22660                     cls: 'separator'
22661                 }
22662             ]
22663         });
22664         
22665     },
22666     
22667     update: function()
22668     {
22669         
22670         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22671         
22672         this.fill();
22673     },
22674     
22675     fill: function() 
22676     {
22677         var hours = this.time.getHours();
22678         var minutes = this.time.getMinutes();
22679         var period = 'AM';
22680         
22681         if(hours > 11){
22682             period = 'PM';
22683         }
22684         
22685         if(hours == 0){
22686             hours = 12;
22687         }
22688         
22689         
22690         if(hours > 12){
22691             hours = hours - 12;
22692         }
22693         
22694         if(hours < 10){
22695             hours = '0' + hours;
22696         }
22697         
22698         if(minutes < 10){
22699             minutes = '0' + minutes;
22700         }
22701         
22702         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22703         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22704         this.pop.select('button', true).first().dom.innerHTML = period;
22705         
22706     },
22707     
22708     place: function()
22709     {   
22710         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22711         
22712         var cls = ['bottom'];
22713         
22714         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22715             cls.pop();
22716             cls.push('top');
22717         }
22718         
22719         cls.push('right');
22720         
22721         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22722             cls.pop();
22723             cls.push('left');
22724         }
22725         //this.picker().setXY(20000,20000);
22726         this.picker().addClass(cls.join('-'));
22727         
22728         var _this = this;
22729         
22730         Roo.each(cls, function(c){
22731             if(c == 'bottom'){
22732                 (function() {
22733                  //  
22734                 }).defer(200);
22735                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22736                 //_this.picker().setTop(_this.inputEl().getHeight());
22737                 return;
22738             }
22739             if(c == 'top'){
22740                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22741                 
22742                 //_this.picker().setTop(0 - _this.picker().getHeight());
22743                 return;
22744             }
22745             /*
22746             if(c == 'left'){
22747                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22748                 return;
22749             }
22750             if(c == 'right'){
22751                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22752                 return;
22753             }
22754             */
22755         });
22756         
22757     },
22758   
22759     onFocus : function()
22760     {
22761         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22762         this.show();
22763     },
22764     
22765     onBlur : function()
22766     {
22767         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22768         this.hide();
22769     },
22770     
22771     show : function()
22772     {
22773         this.picker().show();
22774         this.pop.show();
22775         this.update();
22776         this.place();
22777         
22778         this.fireEvent('show', this, this.date);
22779     },
22780     
22781     hide : function()
22782     {
22783         this.picker().hide();
22784         this.pop.hide();
22785         
22786         this.fireEvent('hide', this, this.date);
22787     },
22788     
22789     setTime : function()
22790     {
22791         this.hide();
22792         this.setValue(this.time.format(this.format));
22793         
22794         this.fireEvent('select', this, this.date);
22795         
22796         
22797     },
22798     
22799     onMousedown: function(e){
22800         e.stopPropagation();
22801         e.preventDefault();
22802     },
22803     
22804     onIncrementHours: function()
22805     {
22806         Roo.log('onIncrementHours');
22807         this.time = this.time.add(Date.HOUR, 1);
22808         this.update();
22809         
22810     },
22811     
22812     onDecrementHours: function()
22813     {
22814         Roo.log('onDecrementHours');
22815         this.time = this.time.add(Date.HOUR, -1);
22816         this.update();
22817     },
22818     
22819     onIncrementMinutes: function()
22820     {
22821         Roo.log('onIncrementMinutes');
22822         this.time = this.time.add(Date.MINUTE, 1);
22823         this.update();
22824     },
22825     
22826     onDecrementMinutes: function()
22827     {
22828         Roo.log('onDecrementMinutes');
22829         this.time = this.time.add(Date.MINUTE, -1);
22830         this.update();
22831     },
22832     
22833     onTogglePeriod: function()
22834     {
22835         Roo.log('onTogglePeriod');
22836         this.time = this.time.add(Date.HOUR, 12);
22837         this.update();
22838     }
22839     
22840    
22841 });
22842  
22843
22844 Roo.apply(Roo.bootstrap.TimeField,  {
22845   
22846     template : {
22847         tag: 'div',
22848         cls: 'datepicker dropdown-menu',
22849         cn: [
22850             {
22851                 tag: 'div',
22852                 cls: 'datepicker-time',
22853                 cn: [
22854                 {
22855                     tag: 'table',
22856                     cls: 'table-condensed',
22857                     cn:[
22858                         {
22859                             tag: 'tbody',
22860                             cn: [
22861                                 {
22862                                     tag: 'tr',
22863                                     cn: [
22864                                     {
22865                                         tag: 'td',
22866                                         colspan: '7'
22867                                     }
22868                                     ]
22869                                 }
22870                             ]
22871                         },
22872                         {
22873                             tag: 'tfoot',
22874                             cn: [
22875                                 {
22876                                     tag: 'tr',
22877                                     cn: [
22878                                     {
22879                                         tag: 'th',
22880                                         colspan: '7',
22881                                         cls: '',
22882                                         cn: [
22883                                             {
22884                                                 tag: 'button',
22885                                                 cls: 'btn btn-info ok',
22886                                                 html: 'OK'
22887                                             }
22888                                         ]
22889                                     }
22890                     
22891                                     ]
22892                                 }
22893                             ]
22894                         }
22895                     ]
22896                 }
22897                 ]
22898             }
22899         ]
22900     }
22901 });
22902
22903  
22904
22905  /*
22906  * - LGPL
22907  *
22908  * MonthField
22909  * 
22910  */
22911
22912 /**
22913  * @class Roo.bootstrap.MonthField
22914  * @extends Roo.bootstrap.Input
22915  * Bootstrap MonthField class
22916  * 
22917  * @cfg {String} language default en
22918  * 
22919  * @constructor
22920  * Create a new MonthField
22921  * @param {Object} config The config object
22922  */
22923
22924 Roo.bootstrap.MonthField = function(config){
22925     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22926     
22927     this.addEvents({
22928         /**
22929          * @event show
22930          * Fires when this field show.
22931          * @param {Roo.bootstrap.MonthField} this
22932          * @param {Mixed} date The date value
22933          */
22934         show : true,
22935         /**
22936          * @event show
22937          * Fires when this field hide.
22938          * @param {Roo.bootstrap.MonthField} this
22939          * @param {Mixed} date The date value
22940          */
22941         hide : true,
22942         /**
22943          * @event select
22944          * Fires when select a date.
22945          * @param {Roo.bootstrap.MonthField} this
22946          * @param {String} oldvalue The old value
22947          * @param {String} newvalue The new value
22948          */
22949         select : true
22950     });
22951 };
22952
22953 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22954     
22955     onRender: function(ct, position)
22956     {
22957         
22958         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22959         
22960         this.language = this.language || 'en';
22961         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22962         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22963         
22964         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22965         this.isInline = false;
22966         this.isInput = true;
22967         this.component = this.el.select('.add-on', true).first() || false;
22968         this.component = (this.component && this.component.length === 0) ? false : this.component;
22969         this.hasInput = this.component && this.inputEL().length;
22970         
22971         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22972         
22973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22974         
22975         this.picker().on('mousedown', this.onMousedown, this);
22976         this.picker().on('click', this.onClick, this);
22977         
22978         this.picker().addClass('datepicker-dropdown');
22979         
22980         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22981             v.setStyle('width', '189px');
22982         });
22983         
22984         this.fillMonths();
22985         
22986         this.update();
22987         
22988         if(this.isInline) {
22989             this.show();
22990         }
22991         
22992     },
22993     
22994     setValue: function(v, suppressEvent)
22995     {   
22996         var o = this.getValue();
22997         
22998         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22999         
23000         this.update();
23001
23002         if(suppressEvent !== true){
23003             this.fireEvent('select', this, o, v);
23004         }
23005         
23006     },
23007     
23008     getValue: function()
23009     {
23010         return this.value;
23011     },
23012     
23013     onClick: function(e) 
23014     {
23015         e.stopPropagation();
23016         e.preventDefault();
23017         
23018         var target = e.getTarget();
23019         
23020         if(target.nodeName.toLowerCase() === 'i'){
23021             target = Roo.get(target).dom.parentNode;
23022         }
23023         
23024         var nodeName = target.nodeName;
23025         var className = target.className;
23026         var html = target.innerHTML;
23027         
23028         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23029             return;
23030         }
23031         
23032         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23033         
23034         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23035         
23036         this.hide();
23037                         
23038     },
23039     
23040     picker : function()
23041     {
23042         return this.pickerEl;
23043     },
23044     
23045     fillMonths: function()
23046     {    
23047         var i = 0;
23048         var months = this.picker().select('>.datepicker-months td', true).first();
23049         
23050         months.dom.innerHTML = '';
23051         
23052         while (i < 12) {
23053             var month = {
23054                 tag: 'span',
23055                 cls: 'month',
23056                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23057             };
23058             
23059             months.createChild(month);
23060         }
23061         
23062     },
23063     
23064     update: function()
23065     {
23066         var _this = this;
23067         
23068         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23069             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23070         }
23071         
23072         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23073             e.removeClass('active');
23074             
23075             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23076                 e.addClass('active');
23077             }
23078         })
23079     },
23080     
23081     place: function()
23082     {
23083         if(this.isInline) {
23084             return;
23085         }
23086         
23087         this.picker().removeClass(['bottom', 'top']);
23088         
23089         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23090             /*
23091              * place to the top of element!
23092              *
23093              */
23094             
23095             this.picker().addClass('top');
23096             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23097             
23098             return;
23099         }
23100         
23101         this.picker().addClass('bottom');
23102         
23103         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23104     },
23105     
23106     onFocus : function()
23107     {
23108         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23109         this.show();
23110     },
23111     
23112     onBlur : function()
23113     {
23114         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23115         
23116         var d = this.inputEl().getValue();
23117         
23118         this.setValue(d);
23119                 
23120         this.hide();
23121     },
23122     
23123     show : function()
23124     {
23125         this.picker().show();
23126         this.picker().select('>.datepicker-months', true).first().show();
23127         this.update();
23128         this.place();
23129         
23130         this.fireEvent('show', this, this.date);
23131     },
23132     
23133     hide : function()
23134     {
23135         if(this.isInline) {
23136             return;
23137         }
23138         this.picker().hide();
23139         this.fireEvent('hide', this, this.date);
23140         
23141     },
23142     
23143     onMousedown: function(e)
23144     {
23145         e.stopPropagation();
23146         e.preventDefault();
23147     },
23148     
23149     keyup: function(e)
23150     {
23151         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23152         this.update();
23153     },
23154
23155     fireKey: function(e)
23156     {
23157         if (!this.picker().isVisible()){
23158             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23159                 this.show();
23160             }
23161             return;
23162         }
23163         
23164         var dir;
23165         
23166         switch(e.keyCode){
23167             case 27: // escape
23168                 this.hide();
23169                 e.preventDefault();
23170                 break;
23171             case 37: // left
23172             case 39: // right
23173                 dir = e.keyCode == 37 ? -1 : 1;
23174                 
23175                 this.vIndex = this.vIndex + dir;
23176                 
23177                 if(this.vIndex < 0){
23178                     this.vIndex = 0;
23179                 }
23180                 
23181                 if(this.vIndex > 11){
23182                     this.vIndex = 11;
23183                 }
23184                 
23185                 if(isNaN(this.vIndex)){
23186                     this.vIndex = 0;
23187                 }
23188                 
23189                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23190                 
23191                 break;
23192             case 38: // up
23193             case 40: // down
23194                 
23195                 dir = e.keyCode == 38 ? -1 : 1;
23196                 
23197                 this.vIndex = this.vIndex + dir * 4;
23198                 
23199                 if(this.vIndex < 0){
23200                     this.vIndex = 0;
23201                 }
23202                 
23203                 if(this.vIndex > 11){
23204                     this.vIndex = 11;
23205                 }
23206                 
23207                 if(isNaN(this.vIndex)){
23208                     this.vIndex = 0;
23209                 }
23210                 
23211                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23212                 break;
23213                 
23214             case 13: // enter
23215                 
23216                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23217                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23218                 }
23219                 
23220                 this.hide();
23221                 e.preventDefault();
23222                 break;
23223             case 9: // tab
23224                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23225                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23226                 }
23227                 this.hide();
23228                 break;
23229             case 16: // shift
23230             case 17: // ctrl
23231             case 18: // alt
23232                 break;
23233             default :
23234                 this.hide();
23235                 
23236         }
23237     },
23238     
23239     remove: function() 
23240     {
23241         this.picker().remove();
23242     }
23243    
23244 });
23245
23246 Roo.apply(Roo.bootstrap.MonthField,  {
23247     
23248     content : {
23249         tag: 'tbody',
23250         cn: [
23251         {
23252             tag: 'tr',
23253             cn: [
23254             {
23255                 tag: 'td',
23256                 colspan: '7'
23257             }
23258             ]
23259         }
23260         ]
23261     },
23262     
23263     dates:{
23264         en: {
23265             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23266             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23267         }
23268     }
23269 });
23270
23271 Roo.apply(Roo.bootstrap.MonthField,  {
23272   
23273     template : {
23274         tag: 'div',
23275         cls: 'datepicker dropdown-menu roo-dynamic',
23276         cn: [
23277             {
23278                 tag: 'div',
23279                 cls: 'datepicker-months',
23280                 cn: [
23281                 {
23282                     tag: 'table',
23283                     cls: 'table-condensed',
23284                     cn:[
23285                         Roo.bootstrap.DateField.content
23286                     ]
23287                 }
23288                 ]
23289             }
23290         ]
23291     }
23292 });
23293
23294  
23295
23296  
23297  /*
23298  * - LGPL
23299  *
23300  * CheckBox
23301  * 
23302  */
23303
23304 /**
23305  * @class Roo.bootstrap.CheckBox
23306  * @extends Roo.bootstrap.Input
23307  * Bootstrap CheckBox class
23308  * 
23309  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23310  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23311  * @cfg {String} boxLabel The text that appears beside the checkbox
23312  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23313  * @cfg {Boolean} checked initnal the element
23314  * @cfg {Boolean} inline inline the element (default false)
23315  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23316  * @cfg {String} tooltip label tooltip
23317  * 
23318  * @constructor
23319  * Create a new CheckBox
23320  * @param {Object} config The config object
23321  */
23322
23323 Roo.bootstrap.CheckBox = function(config){
23324     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23325    
23326     this.addEvents({
23327         /**
23328         * @event check
23329         * Fires when the element is checked or unchecked.
23330         * @param {Roo.bootstrap.CheckBox} this This input
23331         * @param {Boolean} checked The new checked value
23332         */
23333        check : true,
23334        /**
23335         * @event click
23336         * Fires when the element is click.
23337         * @param {Roo.bootstrap.CheckBox} this This input
23338         */
23339        click : true
23340     });
23341     
23342 };
23343
23344 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23345   
23346     inputType: 'checkbox',
23347     inputValue: 1,
23348     valueOff: 0,
23349     boxLabel: false,
23350     checked: false,
23351     weight : false,
23352     inline: false,
23353     tooltip : '',
23354     
23355     // checkbox success does not make any sense really.. 
23356     invalidClass : "",
23357     validClass : "",
23358     
23359     
23360     getAutoCreate : function()
23361     {
23362         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23363         
23364         var id = Roo.id();
23365         
23366         var cfg = {};
23367         
23368         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23369         
23370         if(this.inline){
23371             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23372         }
23373         
23374         var input =  {
23375             tag: 'input',
23376             id : id,
23377             type : this.inputType,
23378             value : this.inputValue,
23379             cls : 'roo-' + this.inputType, //'form-box',
23380             placeholder : this.placeholder || ''
23381             
23382         };
23383         
23384         if(this.inputType != 'radio'){
23385             var hidden =  {
23386                 tag: 'input',
23387                 type : 'hidden',
23388                 cls : 'roo-hidden-value',
23389                 value : this.checked ? this.inputValue : this.valueOff
23390             };
23391         }
23392         
23393             
23394         if (this.weight) { // Validity check?
23395             cfg.cls += " " + this.inputType + "-" + this.weight;
23396         }
23397         
23398         if (this.disabled) {
23399             input.disabled=true;
23400         }
23401         
23402         if(this.checked){
23403             input.checked = this.checked;
23404         }
23405         
23406         if (this.name) {
23407             
23408             input.name = this.name;
23409             
23410             if(this.inputType != 'radio'){
23411                 hidden.name = this.name;
23412                 input.name = '_hidden_' + this.name;
23413             }
23414         }
23415         
23416         if (this.size) {
23417             input.cls += ' input-' + this.size;
23418         }
23419         
23420         var settings=this;
23421         
23422         ['xs','sm','md','lg'].map(function(size){
23423             if (settings[size]) {
23424                 cfg.cls += ' col-' + size + '-' + settings[size];
23425             }
23426         });
23427         
23428         var inputblock = input;
23429          
23430         if (this.before || this.after) {
23431             
23432             inputblock = {
23433                 cls : 'input-group',
23434                 cn :  [] 
23435             };
23436             
23437             if (this.before) {
23438                 inputblock.cn.push({
23439                     tag :'span',
23440                     cls : 'input-group-addon',
23441                     html : this.before
23442                 });
23443             }
23444             
23445             inputblock.cn.push(input);
23446             
23447             if(this.inputType != 'radio'){
23448                 inputblock.cn.push(hidden);
23449             }
23450             
23451             if (this.after) {
23452                 inputblock.cn.push({
23453                     tag :'span',
23454                     cls : 'input-group-addon',
23455                     html : this.after
23456                 });
23457             }
23458             
23459         }
23460         var boxLabelCfg = false;
23461         
23462         if(this.boxLabel){
23463            
23464             boxLabelCfg = {
23465                 tag: 'label',
23466                 //'for': id, // box label is handled by onclick - so no for...
23467                 cls: 'box-label',
23468                 html: this.boxLabel
23469             };
23470             if(this.tooltip){
23471                 boxLabelCfg.tooltip = this.tooltip;
23472             }
23473              
23474         }
23475         
23476         
23477         if (align ==='left' && this.fieldLabel.length) {
23478 //                Roo.log("left and has label");
23479             cfg.cn = [
23480                 {
23481                     tag: 'label',
23482                     'for' :  id,
23483                     cls : 'control-label',
23484                     html : this.fieldLabel
23485                 },
23486                 {
23487                     cls : "", 
23488                     cn: [
23489                         inputblock
23490                     ]
23491                 }
23492             ];
23493             
23494             if (boxLabelCfg) {
23495                 cfg.cn[1].cn.push(boxLabelCfg);
23496             }
23497             
23498             if(this.labelWidth > 12){
23499                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23500             }
23501             
23502             if(this.labelWidth < 13 && this.labelmd == 0){
23503                 this.labelmd = this.labelWidth;
23504             }
23505             
23506             if(this.labellg > 0){
23507                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23508                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23509             }
23510             
23511             if(this.labelmd > 0){
23512                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23513                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23514             }
23515             
23516             if(this.labelsm > 0){
23517                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23518                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23519             }
23520             
23521             if(this.labelxs > 0){
23522                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23523                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23524             }
23525             
23526         } else if ( this.fieldLabel.length) {
23527 //                Roo.log(" label");
23528                 cfg.cn = [
23529                    
23530                     {
23531                         tag: this.boxLabel ? 'span' : 'label',
23532                         'for': id,
23533                         cls: 'control-label box-input-label',
23534                         //cls : 'input-group-addon',
23535                         html : this.fieldLabel
23536                     },
23537                     
23538                     inputblock
23539                     
23540                 ];
23541                 if (boxLabelCfg) {
23542                     cfg.cn.push(boxLabelCfg);
23543                 }
23544
23545         } else {
23546             
23547 //                Roo.log(" no label && no align");
23548                 cfg.cn = [  inputblock ] ;
23549                 if (boxLabelCfg) {
23550                     cfg.cn.push(boxLabelCfg);
23551                 }
23552
23553                 
23554         }
23555         
23556        
23557         
23558         if(this.inputType != 'radio'){
23559             cfg.cn.push(hidden);
23560         }
23561         
23562         return cfg;
23563         
23564     },
23565     
23566     /**
23567      * return the real input element.
23568      */
23569     inputEl: function ()
23570     {
23571         return this.el.select('input.roo-' + this.inputType,true).first();
23572     },
23573     hiddenEl: function ()
23574     {
23575         return this.el.select('input.roo-hidden-value',true).first();
23576     },
23577     
23578     labelEl: function()
23579     {
23580         return this.el.select('label.control-label',true).first();
23581     },
23582     /* depricated... */
23583     
23584     label: function()
23585     {
23586         return this.labelEl();
23587     },
23588     
23589     boxLabelEl: function()
23590     {
23591         return this.el.select('label.box-label',true).first();
23592     },
23593     
23594     initEvents : function()
23595     {
23596 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23597         
23598         this.inputEl().on('click', this.onClick,  this);
23599         
23600         if (this.boxLabel) { 
23601             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23602         }
23603         
23604         this.startValue = this.getValue();
23605         
23606         if(this.groupId){
23607             Roo.bootstrap.CheckBox.register(this);
23608         }
23609     },
23610     
23611     onClick : function(e)
23612     {   
23613         if(this.fireEvent('click', this, e) !== false){
23614             this.setChecked(!this.checked);
23615         }
23616         
23617     },
23618     
23619     setChecked : function(state,suppressEvent)
23620     {
23621         this.startValue = this.getValue();
23622
23623         if(this.inputType == 'radio'){
23624             
23625             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23626                 e.dom.checked = false;
23627             });
23628             
23629             this.inputEl().dom.checked = true;
23630             
23631             this.inputEl().dom.value = this.inputValue;
23632             
23633             if(suppressEvent !== true){
23634                 this.fireEvent('check', this, true);
23635             }
23636             
23637             this.validate();
23638             
23639             return;
23640         }
23641         
23642         this.checked = state;
23643         
23644         this.inputEl().dom.checked = state;
23645         
23646         
23647         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23648         
23649         if(suppressEvent !== true){
23650             this.fireEvent('check', this, state);
23651         }
23652         
23653         this.validate();
23654     },
23655     
23656     getValue : function()
23657     {
23658         if(this.inputType == 'radio'){
23659             return this.getGroupValue();
23660         }
23661         
23662         return this.hiddenEl().dom.value;
23663         
23664     },
23665     
23666     getGroupValue : function()
23667     {
23668         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23669             return '';
23670         }
23671         
23672         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23673     },
23674     
23675     setValue : function(v,suppressEvent)
23676     {
23677         if(this.inputType == 'radio'){
23678             this.setGroupValue(v, suppressEvent);
23679             return;
23680         }
23681         
23682         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23683         
23684         this.validate();
23685     },
23686     
23687     setGroupValue : function(v, suppressEvent)
23688     {
23689         this.startValue = this.getValue();
23690         
23691         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23692             e.dom.checked = false;
23693             
23694             if(e.dom.value == v){
23695                 e.dom.checked = true;
23696             }
23697         });
23698         
23699         if(suppressEvent !== true){
23700             this.fireEvent('check', this, true);
23701         }
23702
23703         this.validate();
23704         
23705         return;
23706     },
23707     
23708     validate : function()
23709     {
23710         if(this.getVisibilityEl().hasClass('hidden')){
23711             return true;
23712         }
23713         
23714         if(
23715                 this.disabled || 
23716                 (this.inputType == 'radio' && this.validateRadio()) ||
23717                 (this.inputType == 'checkbox' && this.validateCheckbox())
23718         ){
23719             this.markValid();
23720             return true;
23721         }
23722         
23723         this.markInvalid();
23724         return false;
23725     },
23726     
23727     validateRadio : function()
23728     {
23729         if(this.getVisibilityEl().hasClass('hidden')){
23730             return true;
23731         }
23732         
23733         if(this.allowBlank){
23734             return true;
23735         }
23736         
23737         var valid = false;
23738         
23739         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23740             if(!e.dom.checked){
23741                 return;
23742             }
23743             
23744             valid = true;
23745             
23746             return false;
23747         });
23748         
23749         return valid;
23750     },
23751     
23752     validateCheckbox : function()
23753     {
23754         if(!this.groupId){
23755             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23756             //return (this.getValue() == this.inputValue) ? true : false;
23757         }
23758         
23759         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23760         
23761         if(!group){
23762             return false;
23763         }
23764         
23765         var r = false;
23766         
23767         for(var i in group){
23768             if(group[i].el.isVisible(true)){
23769                 r = false;
23770                 break;
23771             }
23772             
23773             r = true;
23774         }
23775         
23776         for(var i in group){
23777             if(r){
23778                 break;
23779             }
23780             
23781             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23782         }
23783         
23784         return r;
23785     },
23786     
23787     /**
23788      * Mark this field as valid
23789      */
23790     markValid : function()
23791     {
23792         var _this = this;
23793         
23794         this.fireEvent('valid', this);
23795         
23796         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23797         
23798         if(this.groupId){
23799             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23800         }
23801         
23802         if(label){
23803             label.markValid();
23804         }
23805
23806         if(this.inputType == 'radio'){
23807             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23808                 var fg = e.findParent('.form-group', false, true);
23809                 if (Roo.bootstrap.version == 3) {
23810                     fg.removeClass([_this.invalidClass, _this.validClass]);
23811                     fg.addClass(_this.validClass);
23812                 } else {
23813                     fg.removeClass(['is-valid', 'is-invalid']);
23814                     fg.addClass('is-valid');
23815                 }
23816             });
23817             
23818             return;
23819         }
23820
23821         if(!this.groupId){
23822             var fg = this.el.findParent('.form-group', false, true);
23823             if (Roo.bootstrap.version == 3) {
23824                 fg.removeClass([this.invalidClass, this.validClass]);
23825                 fg.addClass(this.validClass);
23826             } else {
23827                 fg.removeClass(['is-valid', 'is-invalid']);
23828                 fg.addClass('is-valid');
23829             }
23830             return;
23831         }
23832         
23833         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23834         
23835         if(!group){
23836             return;
23837         }
23838         
23839         for(var i in group){
23840             var fg = group[i].el.findParent('.form-group', false, true);
23841             if (Roo.bootstrap.version == 3) {
23842                 fg.removeClass([this.invalidClass, this.validClass]);
23843                 fg.addClass(this.validClass);
23844             } else {
23845                 fg.removeClass(['is-valid', 'is-invalid']);
23846                 fg.addClass('is-valid');
23847             }
23848         }
23849     },
23850     
23851      /**
23852      * Mark this field as invalid
23853      * @param {String} msg The validation message
23854      */
23855     markInvalid : function(msg)
23856     {
23857         if(this.allowBlank){
23858             return;
23859         }
23860         
23861         var _this = this;
23862         
23863         this.fireEvent('invalid', this, msg);
23864         
23865         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23866         
23867         if(this.groupId){
23868             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23869         }
23870         
23871         if(label){
23872             label.markInvalid();
23873         }
23874             
23875         if(this.inputType == 'radio'){
23876             
23877             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878                 var fg = e.findParent('.form-group', false, true);
23879                 if (Roo.bootstrap.version == 3) {
23880                     fg.removeClass([_this.invalidClass, _this.validClass]);
23881                     fg.addClass(_this.invalidClass);
23882                 } else {
23883                     fg.removeClass(['is-invalid', 'is-valid']);
23884                     fg.addClass('is-invalid');
23885                 }
23886             });
23887             
23888             return;
23889         }
23890         
23891         if(!this.groupId){
23892             var fg = this.el.findParent('.form-group', false, true);
23893             if (Roo.bootstrap.version == 3) {
23894                 fg.removeClass([_this.invalidClass, _this.validClass]);
23895                 fg.addClass(_this.invalidClass);
23896             } else {
23897                 fg.removeClass(['is-invalid', 'is-valid']);
23898                 fg.addClass('is-invalid');
23899             }
23900             return;
23901         }
23902         
23903         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23904         
23905         if(!group){
23906             return;
23907         }
23908         
23909         for(var i in group){
23910             var fg = group[i].el.findParent('.form-group', false, true);
23911             if (Roo.bootstrap.version == 3) {
23912                 fg.removeClass([_this.invalidClass, _this.validClass]);
23913                 fg.addClass(_this.invalidClass);
23914             } else {
23915                 fg.removeClass(['is-invalid', 'is-valid']);
23916                 fg.addClass('is-invalid');
23917             }
23918         }
23919         
23920     },
23921     
23922     clearInvalid : function()
23923     {
23924         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23925         
23926         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23927         
23928         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23929         
23930         if (label && label.iconEl) {
23931             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23932             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23933         }
23934     },
23935     
23936     disable : function()
23937     {
23938         if(this.inputType != 'radio'){
23939             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23940             return;
23941         }
23942         
23943         var _this = this;
23944         
23945         if(this.rendered){
23946             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23947                 _this.getActionEl().addClass(this.disabledClass);
23948                 e.dom.disabled = true;
23949             });
23950         }
23951         
23952         this.disabled = true;
23953         this.fireEvent("disable", this);
23954         return this;
23955     },
23956
23957     enable : function()
23958     {
23959         if(this.inputType != 'radio'){
23960             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23961             return;
23962         }
23963         
23964         var _this = this;
23965         
23966         if(this.rendered){
23967             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23968                 _this.getActionEl().removeClass(this.disabledClass);
23969                 e.dom.disabled = false;
23970             });
23971         }
23972         
23973         this.disabled = false;
23974         this.fireEvent("enable", this);
23975         return this;
23976     },
23977     
23978     setBoxLabel : function(v)
23979     {
23980         this.boxLabel = v;
23981         
23982         if(this.rendered){
23983             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23984         }
23985     }
23986
23987 });
23988
23989 Roo.apply(Roo.bootstrap.CheckBox, {
23990     
23991     groups: {},
23992     
23993      /**
23994     * register a CheckBox Group
23995     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23996     */
23997     register : function(checkbox)
23998     {
23999         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24000             this.groups[checkbox.groupId] = {};
24001         }
24002         
24003         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24004             return;
24005         }
24006         
24007         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24008         
24009     },
24010     /**
24011     * fetch a CheckBox Group based on the group ID
24012     * @param {string} the group ID
24013     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24014     */
24015     get: function(groupId) {
24016         if (typeof(this.groups[groupId]) == 'undefined') {
24017             return false;
24018         }
24019         
24020         return this.groups[groupId] ;
24021     }
24022     
24023     
24024 });
24025 /*
24026  * - LGPL
24027  *
24028  * RadioItem
24029  * 
24030  */
24031
24032 /**
24033  * @class Roo.bootstrap.Radio
24034  * @extends Roo.bootstrap.Component
24035  * Bootstrap Radio class
24036  * @cfg {String} boxLabel - the label associated
24037  * @cfg {String} value - the value of radio
24038  * 
24039  * @constructor
24040  * Create a new Radio
24041  * @param {Object} config The config object
24042  */
24043 Roo.bootstrap.Radio = function(config){
24044     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24045     
24046 };
24047
24048 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24049     
24050     boxLabel : '',
24051     
24052     value : '',
24053     
24054     getAutoCreate : function()
24055     {
24056         var cfg = {
24057             tag : 'div',
24058             cls : 'form-group radio',
24059             cn : [
24060                 {
24061                     tag : 'label',
24062                     cls : 'box-label',
24063                     html : this.boxLabel
24064                 }
24065             ]
24066         };
24067         
24068         return cfg;
24069     },
24070     
24071     initEvents : function() 
24072     {
24073         this.parent().register(this);
24074         
24075         this.el.on('click', this.onClick, this);
24076         
24077     },
24078     
24079     onClick : function(e)
24080     {
24081         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24082             this.setChecked(true);
24083         }
24084     },
24085     
24086     setChecked : function(state, suppressEvent)
24087     {
24088         this.parent().setValue(this.value, suppressEvent);
24089         
24090     },
24091     
24092     setBoxLabel : function(v)
24093     {
24094         this.boxLabel = v;
24095         
24096         if(this.rendered){
24097             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24098         }
24099     }
24100     
24101 });
24102  
24103
24104  /*
24105  * - LGPL
24106  *
24107  * Input
24108  * 
24109  */
24110
24111 /**
24112  * @class Roo.bootstrap.SecurePass
24113  * @extends Roo.bootstrap.Input
24114  * Bootstrap SecurePass class
24115  *
24116  * 
24117  * @constructor
24118  * Create a new SecurePass
24119  * @param {Object} config The config object
24120  */
24121  
24122 Roo.bootstrap.SecurePass = function (config) {
24123     // these go here, so the translation tool can replace them..
24124     this.errors = {
24125         PwdEmpty: "Please type a password, and then retype it to confirm.",
24126         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24127         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24128         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24129         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24130         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24131         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24132         TooWeak: "Your password is Too Weak."
24133     },
24134     this.meterLabel = "Password strength:";
24135     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24136     this.meterClass = [
24137         "roo-password-meter-tooweak", 
24138         "roo-password-meter-weak", 
24139         "roo-password-meter-medium", 
24140         "roo-password-meter-strong", 
24141         "roo-password-meter-grey"
24142     ];
24143     
24144     this.errors = {};
24145     
24146     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24147 }
24148
24149 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24150     /**
24151      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24152      * {
24153      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24154      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24155      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24156      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24157      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24158      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24159      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24160      * })
24161      */
24162     // private
24163     
24164     meterWidth: 300,
24165     errorMsg :'',    
24166     errors: false,
24167     imageRoot: '/',
24168     /**
24169      * @cfg {String/Object} Label for the strength meter (defaults to
24170      * 'Password strength:')
24171      */
24172     // private
24173     meterLabel: '',
24174     /**
24175      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24176      * ['Weak', 'Medium', 'Strong'])
24177      */
24178     // private    
24179     pwdStrengths: false,    
24180     // private
24181     strength: 0,
24182     // private
24183     _lastPwd: null,
24184     // private
24185     kCapitalLetter: 0,
24186     kSmallLetter: 1,
24187     kDigit: 2,
24188     kPunctuation: 3,
24189     
24190     insecure: false,
24191     // private
24192     initEvents: function ()
24193     {
24194         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24195
24196         if (this.el.is('input[type=password]') && Roo.isSafari) {
24197             this.el.on('keydown', this.SafariOnKeyDown, this);
24198         }
24199
24200         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24201     },
24202     // private
24203     onRender: function (ct, position)
24204     {
24205         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24206         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24207         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24208
24209         this.trigger.createChild({
24210                    cn: [
24211                     {
24212                     //id: 'PwdMeter',
24213                     tag: 'div',
24214                     cls: 'roo-password-meter-grey col-xs-12',
24215                     style: {
24216                         //width: 0,
24217                         //width: this.meterWidth + 'px'                                                
24218                         }
24219                     },
24220                     {                            
24221                          cls: 'roo-password-meter-text'                          
24222                     }
24223                 ]            
24224         });
24225
24226          
24227         if (this.hideTrigger) {
24228             this.trigger.setDisplayed(false);
24229         }
24230         this.setSize(this.width || '', this.height || '');
24231     },
24232     // private
24233     onDestroy: function ()
24234     {
24235         if (this.trigger) {
24236             this.trigger.removeAllListeners();
24237             this.trigger.remove();
24238         }
24239         if (this.wrap) {
24240             this.wrap.remove();
24241         }
24242         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24243     },
24244     // private
24245     checkStrength: function ()
24246     {
24247         var pwd = this.inputEl().getValue();
24248         if (pwd == this._lastPwd) {
24249             return;
24250         }
24251
24252         var strength;
24253         if (this.ClientSideStrongPassword(pwd)) {
24254             strength = 3;
24255         } else if (this.ClientSideMediumPassword(pwd)) {
24256             strength = 2;
24257         } else if (this.ClientSideWeakPassword(pwd)) {
24258             strength = 1;
24259         } else {
24260             strength = 0;
24261         }
24262         
24263         Roo.log('strength1: ' + strength);
24264         
24265         //var pm = this.trigger.child('div/div/div').dom;
24266         var pm = this.trigger.child('div/div');
24267         pm.removeClass(this.meterClass);
24268         pm.addClass(this.meterClass[strength]);
24269                 
24270         
24271         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24272                 
24273         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24274         
24275         this._lastPwd = pwd;
24276     },
24277     reset: function ()
24278     {
24279         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24280         
24281         this._lastPwd = '';
24282         
24283         var pm = this.trigger.child('div/div');
24284         pm.removeClass(this.meterClass);
24285         pm.addClass('roo-password-meter-grey');        
24286         
24287         
24288         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24289         
24290         pt.innerHTML = '';
24291         this.inputEl().dom.type='password';
24292     },
24293     // private
24294     validateValue: function (value)
24295     {
24296         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24297             return false;
24298         }
24299         if (value.length == 0) {
24300             if (this.allowBlank) {
24301                 this.clearInvalid();
24302                 return true;
24303             }
24304
24305             this.markInvalid(this.errors.PwdEmpty);
24306             this.errorMsg = this.errors.PwdEmpty;
24307             return false;
24308         }
24309         
24310         if(this.insecure){
24311             return true;
24312         }
24313         
24314         if (!value.match(/[\x21-\x7e]+/)) {
24315             this.markInvalid(this.errors.PwdBadChar);
24316             this.errorMsg = this.errors.PwdBadChar;
24317             return false;
24318         }
24319         if (value.length < 6) {
24320             this.markInvalid(this.errors.PwdShort);
24321             this.errorMsg = this.errors.PwdShort;
24322             return false;
24323         }
24324         if (value.length > 16) {
24325             this.markInvalid(this.errors.PwdLong);
24326             this.errorMsg = this.errors.PwdLong;
24327             return false;
24328         }
24329         var strength;
24330         if (this.ClientSideStrongPassword(value)) {
24331             strength = 3;
24332         } else if (this.ClientSideMediumPassword(value)) {
24333             strength = 2;
24334         } else if (this.ClientSideWeakPassword(value)) {
24335             strength = 1;
24336         } else {
24337             strength = 0;
24338         }
24339
24340         
24341         if (strength < 2) {
24342             //this.markInvalid(this.errors.TooWeak);
24343             this.errorMsg = this.errors.TooWeak;
24344             //return false;
24345         }
24346         
24347         
24348         console.log('strength2: ' + strength);
24349         
24350         //var pm = this.trigger.child('div/div/div').dom;
24351         
24352         var pm = this.trigger.child('div/div');
24353         pm.removeClass(this.meterClass);
24354         pm.addClass(this.meterClass[strength]);
24355                 
24356         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24357                 
24358         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24359         
24360         this.errorMsg = ''; 
24361         return true;
24362     },
24363     // private
24364     CharacterSetChecks: function (type)
24365     {
24366         this.type = type;
24367         this.fResult = false;
24368     },
24369     // private
24370     isctype: function (character, type)
24371     {
24372         switch (type) {  
24373             case this.kCapitalLetter:
24374                 if (character >= 'A' && character <= 'Z') {
24375                     return true;
24376                 }
24377                 break;
24378             
24379             case this.kSmallLetter:
24380                 if (character >= 'a' && character <= 'z') {
24381                     return true;
24382                 }
24383                 break;
24384             
24385             case this.kDigit:
24386                 if (character >= '0' && character <= '9') {
24387                     return true;
24388                 }
24389                 break;
24390             
24391             case this.kPunctuation:
24392                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24393                     return true;
24394                 }
24395                 break;
24396             
24397             default:
24398                 return false;
24399         }
24400
24401     },
24402     // private
24403     IsLongEnough: function (pwd, size)
24404     {
24405         return !(pwd == null || isNaN(size) || pwd.length < size);
24406     },
24407     // private
24408     SpansEnoughCharacterSets: function (word, nb)
24409     {
24410         if (!this.IsLongEnough(word, nb))
24411         {
24412             return false;
24413         }
24414
24415         var characterSetChecks = new Array(
24416             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24417             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24418         );
24419         
24420         for (var index = 0; index < word.length; ++index) {
24421             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24422                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24423                     characterSetChecks[nCharSet].fResult = true;
24424                     break;
24425                 }
24426             }
24427         }
24428
24429         var nCharSets = 0;
24430         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24431             if (characterSetChecks[nCharSet].fResult) {
24432                 ++nCharSets;
24433             }
24434         }
24435
24436         if (nCharSets < nb) {
24437             return false;
24438         }
24439         return true;
24440     },
24441     // private
24442     ClientSideStrongPassword: function (pwd)
24443     {
24444         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24445     },
24446     // private
24447     ClientSideMediumPassword: function (pwd)
24448     {
24449         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24450     },
24451     // private
24452     ClientSideWeakPassword: function (pwd)
24453     {
24454         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24455     }
24456           
24457 })//<script type="text/javascript">
24458
24459 /*
24460  * Based  Ext JS Library 1.1.1
24461  * Copyright(c) 2006-2007, Ext JS, LLC.
24462  * LGPL
24463  *
24464  */
24465  
24466 /**
24467  * @class Roo.HtmlEditorCore
24468  * @extends Roo.Component
24469  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24470  *
24471  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24472  */
24473
24474 Roo.HtmlEditorCore = function(config){
24475     
24476     
24477     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24478     
24479     
24480     this.addEvents({
24481         /**
24482          * @event initialize
24483          * Fires when the editor is fully initialized (including the iframe)
24484          * @param {Roo.HtmlEditorCore} this
24485          */
24486         initialize: true,
24487         /**
24488          * @event activate
24489          * Fires when the editor is first receives the focus. Any insertion must wait
24490          * until after this event.
24491          * @param {Roo.HtmlEditorCore} this
24492          */
24493         activate: true,
24494          /**
24495          * @event beforesync
24496          * Fires before the textarea is updated with content from the editor iframe. Return false
24497          * to cancel the sync.
24498          * @param {Roo.HtmlEditorCore} this
24499          * @param {String} html
24500          */
24501         beforesync: true,
24502          /**
24503          * @event beforepush
24504          * Fires before the iframe editor is updated with content from the textarea. Return false
24505          * to cancel the push.
24506          * @param {Roo.HtmlEditorCore} this
24507          * @param {String} html
24508          */
24509         beforepush: true,
24510          /**
24511          * @event sync
24512          * Fires when the textarea is updated with content from the editor iframe.
24513          * @param {Roo.HtmlEditorCore} this
24514          * @param {String} html
24515          */
24516         sync: true,
24517          /**
24518          * @event push
24519          * Fires when the iframe editor is updated with content from the textarea.
24520          * @param {Roo.HtmlEditorCore} this
24521          * @param {String} html
24522          */
24523         push: true,
24524         
24525         /**
24526          * @event editorevent
24527          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24528          * @param {Roo.HtmlEditorCore} this
24529          */
24530         editorevent: true
24531         
24532     });
24533     
24534     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24535     
24536     // defaults : white / black...
24537     this.applyBlacklists();
24538     
24539     
24540     
24541 };
24542
24543
24544 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24545
24546
24547      /**
24548      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24549      */
24550     
24551     owner : false,
24552     
24553      /**
24554      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24555      *                        Roo.resizable.
24556      */
24557     resizable : false,
24558      /**
24559      * @cfg {Number} height (in pixels)
24560      */   
24561     height: 300,
24562    /**
24563      * @cfg {Number} width (in pixels)
24564      */   
24565     width: 500,
24566     
24567     /**
24568      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24569      * 
24570      */
24571     stylesheets: false,
24572     
24573     // id of frame..
24574     frameId: false,
24575     
24576     // private properties
24577     validationEvent : false,
24578     deferHeight: true,
24579     initialized : false,
24580     activated : false,
24581     sourceEditMode : false,
24582     onFocus : Roo.emptyFn,
24583     iframePad:3,
24584     hideMode:'offsets',
24585     
24586     clearUp: true,
24587     
24588     // blacklist + whitelisted elements..
24589     black: false,
24590     white: false,
24591      
24592     bodyCls : '',
24593
24594     /**
24595      * Protected method that will not generally be called directly. It
24596      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24597      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24598      */
24599     getDocMarkup : function(){
24600         // body styles..
24601         var st = '';
24602         
24603         // inherit styels from page...?? 
24604         if (this.stylesheets === false) {
24605             
24606             Roo.get(document.head).select('style').each(function(node) {
24607                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24608             });
24609             
24610             Roo.get(document.head).select('link').each(function(node) { 
24611                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24612             });
24613             
24614         } else if (!this.stylesheets.length) {
24615                 // simple..
24616                 st = '<style type="text/css">' +
24617                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24618                    '</style>';
24619         } else {
24620             for (var i in this.stylesheets) { 
24621                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24622             }
24623             
24624         }
24625         
24626         st +=  '<style type="text/css">' +
24627             'IMG { cursor: pointer } ' +
24628         '</style>';
24629
24630         var cls = 'roo-htmleditor-body';
24631         
24632         if(this.bodyCls.length){
24633             cls += ' ' + this.bodyCls;
24634         }
24635         
24636         return '<html><head>' + st  +
24637             //<style type="text/css">' +
24638             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24639             //'</style>' +
24640             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24641     },
24642
24643     // private
24644     onRender : function(ct, position)
24645     {
24646         var _t = this;
24647         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24648         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24649         
24650         
24651         this.el.dom.style.border = '0 none';
24652         this.el.dom.setAttribute('tabIndex', -1);
24653         this.el.addClass('x-hidden hide');
24654         
24655         
24656         
24657         if(Roo.isIE){ // fix IE 1px bogus margin
24658             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24659         }
24660        
24661         
24662         this.frameId = Roo.id();
24663         
24664          
24665         
24666         var iframe = this.owner.wrap.createChild({
24667             tag: 'iframe',
24668             cls: 'form-control', // bootstrap..
24669             id: this.frameId,
24670             name: this.frameId,
24671             frameBorder : 'no',
24672             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24673         }, this.el
24674         );
24675         
24676         
24677         this.iframe = iframe.dom;
24678
24679          this.assignDocWin();
24680         
24681         this.doc.designMode = 'on';
24682        
24683         this.doc.open();
24684         this.doc.write(this.getDocMarkup());
24685         this.doc.close();
24686
24687         
24688         var task = { // must defer to wait for browser to be ready
24689             run : function(){
24690                 //console.log("run task?" + this.doc.readyState);
24691                 this.assignDocWin();
24692                 if(this.doc.body || this.doc.readyState == 'complete'){
24693                     try {
24694                         this.doc.designMode="on";
24695                     } catch (e) {
24696                         return;
24697                     }
24698                     Roo.TaskMgr.stop(task);
24699                     this.initEditor.defer(10, this);
24700                 }
24701             },
24702             interval : 10,
24703             duration: 10000,
24704             scope: this
24705         };
24706         Roo.TaskMgr.start(task);
24707
24708     },
24709
24710     // private
24711     onResize : function(w, h)
24712     {
24713          Roo.log('resize: ' +w + ',' + h );
24714         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24715         if(!this.iframe){
24716             return;
24717         }
24718         if(typeof w == 'number'){
24719             
24720             this.iframe.style.width = w + 'px';
24721         }
24722         if(typeof h == 'number'){
24723             
24724             this.iframe.style.height = h + 'px';
24725             if(this.doc){
24726                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24727             }
24728         }
24729         
24730     },
24731
24732     /**
24733      * Toggles the editor between standard and source edit mode.
24734      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24735      */
24736     toggleSourceEdit : function(sourceEditMode){
24737         
24738         this.sourceEditMode = sourceEditMode === true;
24739         
24740         if(this.sourceEditMode){
24741  
24742             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24743             
24744         }else{
24745             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24746             //this.iframe.className = '';
24747             this.deferFocus();
24748         }
24749         //this.setSize(this.owner.wrap.getSize());
24750         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24751     },
24752
24753     
24754   
24755
24756     /**
24757      * Protected method that will not generally be called directly. If you need/want
24758      * custom HTML cleanup, this is the method you should override.
24759      * @param {String} html The HTML to be cleaned
24760      * return {String} The cleaned HTML
24761      */
24762     cleanHtml : function(html){
24763         html = String(html);
24764         if(html.length > 5){
24765             if(Roo.isSafari){ // strip safari nonsense
24766                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24767             }
24768         }
24769         if(html == '&nbsp;'){
24770             html = '';
24771         }
24772         return html;
24773     },
24774
24775     /**
24776      * HTML Editor -> Textarea
24777      * Protected method that will not generally be called directly. Syncs the contents
24778      * of the editor iframe with the textarea.
24779      */
24780     syncValue : function(){
24781         if(this.initialized){
24782             var bd = (this.doc.body || this.doc.documentElement);
24783             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24784             var html = bd.innerHTML;
24785             if(Roo.isSafari){
24786                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24787                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24788                 if(m && m[1]){
24789                     html = '<div style="'+m[0]+'">' + html + '</div>';
24790                 }
24791             }
24792             html = this.cleanHtml(html);
24793             // fix up the special chars.. normaly like back quotes in word...
24794             // however we do not want to do this with chinese..
24795             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24796                 
24797                 var cc = match.charCodeAt();
24798
24799                 // Get the character value, handling surrogate pairs
24800                 if (match.length == 2) {
24801                     // It's a surrogate pair, calculate the Unicode code point
24802                     var high = match.charCodeAt(0) - 0xD800;
24803                     var low  = match.charCodeAt(1) - 0xDC00;
24804                     cc = (high * 0x400) + low + 0x10000;
24805                 }  else if (
24806                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24807                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24808                     (cc >= 0xf900 && cc < 0xfb00 )
24809                 ) {
24810                         return match;
24811                 }  
24812          
24813                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24814                 return "&#" + cc + ";";
24815                 
24816                 
24817             });
24818             
24819             
24820              
24821             if(this.owner.fireEvent('beforesync', this, html) !== false){
24822                 this.el.dom.value = html;
24823                 this.owner.fireEvent('sync', this, html);
24824             }
24825         }
24826     },
24827
24828     /**
24829      * Protected method that will not generally be called directly. Pushes the value of the textarea
24830      * into the iframe editor.
24831      */
24832     pushValue : function(){
24833         if(this.initialized){
24834             var v = this.el.dom.value.trim();
24835             
24836 //            if(v.length < 1){
24837 //                v = '&#160;';
24838 //            }
24839             
24840             if(this.owner.fireEvent('beforepush', this, v) !== false){
24841                 var d = (this.doc.body || this.doc.documentElement);
24842                 d.innerHTML = v;
24843                 this.cleanUpPaste();
24844                 this.el.dom.value = d.innerHTML;
24845                 this.owner.fireEvent('push', this, v);
24846             }
24847         }
24848     },
24849
24850     // private
24851     deferFocus : function(){
24852         this.focus.defer(10, this);
24853     },
24854
24855     // doc'ed in Field
24856     focus : function(){
24857         if(this.win && !this.sourceEditMode){
24858             this.win.focus();
24859         }else{
24860             this.el.focus();
24861         }
24862     },
24863     
24864     assignDocWin: function()
24865     {
24866         var iframe = this.iframe;
24867         
24868          if(Roo.isIE){
24869             this.doc = iframe.contentWindow.document;
24870             this.win = iframe.contentWindow;
24871         } else {
24872 //            if (!Roo.get(this.frameId)) {
24873 //                return;
24874 //            }
24875 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24876 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24877             
24878             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24879                 return;
24880             }
24881             
24882             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24883             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24884         }
24885     },
24886     
24887     // private
24888     initEditor : function(){
24889         //console.log("INIT EDITOR");
24890         this.assignDocWin();
24891         
24892         
24893         
24894         this.doc.designMode="on";
24895         this.doc.open();
24896         this.doc.write(this.getDocMarkup());
24897         this.doc.close();
24898         
24899         var dbody = (this.doc.body || this.doc.documentElement);
24900         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24901         // this copies styles from the containing element into thsi one..
24902         // not sure why we need all of this..
24903         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24904         
24905         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24906         //ss['background-attachment'] = 'fixed'; // w3c
24907         dbody.bgProperties = 'fixed'; // ie
24908         //Roo.DomHelper.applyStyles(dbody, ss);
24909         Roo.EventManager.on(this.doc, {
24910             //'mousedown': this.onEditorEvent,
24911             'mouseup': this.onEditorEvent,
24912             'dblclick': this.onEditorEvent,
24913             'click': this.onEditorEvent,
24914             'keyup': this.onEditorEvent,
24915             buffer:100,
24916             scope: this
24917         });
24918         if(Roo.isGecko){
24919             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24920         }
24921         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24922             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24923         }
24924         this.initialized = true;
24925
24926         this.owner.fireEvent('initialize', this);
24927         this.pushValue();
24928     },
24929
24930     // private
24931     onDestroy : function(){
24932         
24933         
24934         
24935         if(this.rendered){
24936             
24937             //for (var i =0; i < this.toolbars.length;i++) {
24938             //    // fixme - ask toolbars for heights?
24939             //    this.toolbars[i].onDestroy();
24940            // }
24941             
24942             //this.wrap.dom.innerHTML = '';
24943             //this.wrap.remove();
24944         }
24945     },
24946
24947     // private
24948     onFirstFocus : function(){
24949         
24950         this.assignDocWin();
24951         
24952         
24953         this.activated = true;
24954          
24955     
24956         if(Roo.isGecko){ // prevent silly gecko errors
24957             this.win.focus();
24958             var s = this.win.getSelection();
24959             if(!s.focusNode || s.focusNode.nodeType != 3){
24960                 var r = s.getRangeAt(0);
24961                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24962                 r.collapse(true);
24963                 this.deferFocus();
24964             }
24965             try{
24966                 this.execCmd('useCSS', true);
24967                 this.execCmd('styleWithCSS', false);
24968             }catch(e){}
24969         }
24970         this.owner.fireEvent('activate', this);
24971     },
24972
24973     // private
24974     adjustFont: function(btn){
24975         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24976         //if(Roo.isSafari){ // safari
24977         //    adjust *= 2;
24978        // }
24979         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24980         if(Roo.isSafari){ // safari
24981             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24982             v =  (v < 10) ? 10 : v;
24983             v =  (v > 48) ? 48 : v;
24984             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24985             
24986         }
24987         
24988         
24989         v = Math.max(1, v+adjust);
24990         
24991         this.execCmd('FontSize', v  );
24992     },
24993
24994     onEditorEvent : function(e)
24995     {
24996         this.owner.fireEvent('editorevent', this, e);
24997       //  this.updateToolbar();
24998         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24999     },
25000
25001     insertTag : function(tg)
25002     {
25003         // could be a bit smarter... -> wrap the current selected tRoo..
25004         if (tg.toLowerCase() == 'span' ||
25005             tg.toLowerCase() == 'code' ||
25006             tg.toLowerCase() == 'sup' ||
25007             tg.toLowerCase() == 'sub' 
25008             ) {
25009             
25010             range = this.createRange(this.getSelection());
25011             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25012             wrappingNode.appendChild(range.extractContents());
25013             range.insertNode(wrappingNode);
25014
25015             return;
25016             
25017             
25018             
25019         }
25020         this.execCmd("formatblock",   tg);
25021         
25022     },
25023     
25024     insertText : function(txt)
25025     {
25026         
25027         
25028         var range = this.createRange();
25029         range.deleteContents();
25030                //alert(Sender.getAttribute('label'));
25031                
25032         range.insertNode(this.doc.createTextNode(txt));
25033     } ,
25034     
25035      
25036
25037     /**
25038      * Executes a Midas editor command on the editor document and performs necessary focus and
25039      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25040      * @param {String} cmd The Midas command
25041      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25042      */
25043     relayCmd : function(cmd, value){
25044         this.win.focus();
25045         this.execCmd(cmd, value);
25046         this.owner.fireEvent('editorevent', this);
25047         //this.updateToolbar();
25048         this.owner.deferFocus();
25049     },
25050
25051     /**
25052      * Executes a Midas editor command directly on the editor document.
25053      * For visual commands, you should use {@link #relayCmd} instead.
25054      * <b>This should only be called after the editor is initialized.</b>
25055      * @param {String} cmd The Midas command
25056      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25057      */
25058     execCmd : function(cmd, value){
25059         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25060         this.syncValue();
25061     },
25062  
25063  
25064    
25065     /**
25066      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25067      * to insert tRoo.
25068      * @param {String} text | dom node.. 
25069      */
25070     insertAtCursor : function(text)
25071     {
25072         
25073         if(!this.activated){
25074             return;
25075         }
25076         /*
25077         if(Roo.isIE){
25078             this.win.focus();
25079             var r = this.doc.selection.createRange();
25080             if(r){
25081                 r.collapse(true);
25082                 r.pasteHTML(text);
25083                 this.syncValue();
25084                 this.deferFocus();
25085             
25086             }
25087             return;
25088         }
25089         */
25090         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25091             this.win.focus();
25092             
25093             
25094             // from jquery ui (MIT licenced)
25095             var range, node;
25096             var win = this.win;
25097             
25098             if (win.getSelection && win.getSelection().getRangeAt) {
25099                 range = win.getSelection().getRangeAt(0);
25100                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25101                 range.insertNode(node);
25102             } else if (win.document.selection && win.document.selection.createRange) {
25103                 // no firefox support
25104                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25105                 win.document.selection.createRange().pasteHTML(txt);
25106             } else {
25107                 // no firefox support
25108                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25109                 this.execCmd('InsertHTML', txt);
25110             } 
25111             
25112             this.syncValue();
25113             
25114             this.deferFocus();
25115         }
25116     },
25117  // private
25118     mozKeyPress : function(e){
25119         if(e.ctrlKey){
25120             var c = e.getCharCode(), cmd;
25121           
25122             if(c > 0){
25123                 c = String.fromCharCode(c).toLowerCase();
25124                 switch(c){
25125                     case 'b':
25126                         cmd = 'bold';
25127                         break;
25128                     case 'i':
25129                         cmd = 'italic';
25130                         break;
25131                     
25132                     case 'u':
25133                         cmd = 'underline';
25134                         break;
25135                     
25136                     case 'v':
25137                         this.cleanUpPaste.defer(100, this);
25138                         return;
25139                         
25140                 }
25141                 if(cmd){
25142                     this.win.focus();
25143                     this.execCmd(cmd);
25144                     this.deferFocus();
25145                     e.preventDefault();
25146                 }
25147                 
25148             }
25149         }
25150     },
25151
25152     // private
25153     fixKeys : function(){ // load time branching for fastest keydown performance
25154         if(Roo.isIE){
25155             return function(e){
25156                 var k = e.getKey(), r;
25157                 if(k == e.TAB){
25158                     e.stopEvent();
25159                     r = this.doc.selection.createRange();
25160                     if(r){
25161                         r.collapse(true);
25162                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25163                         this.deferFocus();
25164                     }
25165                     return;
25166                 }
25167                 
25168                 if(k == e.ENTER){
25169                     r = this.doc.selection.createRange();
25170                     if(r){
25171                         var target = r.parentElement();
25172                         if(!target || target.tagName.toLowerCase() != 'li'){
25173                             e.stopEvent();
25174                             r.pasteHTML('<br />');
25175                             r.collapse(false);
25176                             r.select();
25177                         }
25178                     }
25179                 }
25180                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25181                     this.cleanUpPaste.defer(100, this);
25182                     return;
25183                 }
25184                 
25185                 
25186             };
25187         }else if(Roo.isOpera){
25188             return function(e){
25189                 var k = e.getKey();
25190                 if(k == e.TAB){
25191                     e.stopEvent();
25192                     this.win.focus();
25193                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25194                     this.deferFocus();
25195                 }
25196                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25197                     this.cleanUpPaste.defer(100, this);
25198                     return;
25199                 }
25200                 
25201             };
25202         }else if(Roo.isSafari){
25203             return function(e){
25204                 var k = e.getKey();
25205                 
25206                 if(k == e.TAB){
25207                     e.stopEvent();
25208                     this.execCmd('InsertText','\t');
25209                     this.deferFocus();
25210                     return;
25211                 }
25212                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25213                     this.cleanUpPaste.defer(100, this);
25214                     return;
25215                 }
25216                 
25217              };
25218         }
25219     }(),
25220     
25221     getAllAncestors: function()
25222     {
25223         var p = this.getSelectedNode();
25224         var a = [];
25225         if (!p) {
25226             a.push(p); // push blank onto stack..
25227             p = this.getParentElement();
25228         }
25229         
25230         
25231         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25232             a.push(p);
25233             p = p.parentNode;
25234         }
25235         a.push(this.doc.body);
25236         return a;
25237     },
25238     lastSel : false,
25239     lastSelNode : false,
25240     
25241     
25242     getSelection : function() 
25243     {
25244         this.assignDocWin();
25245         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25246     },
25247     
25248     getSelectedNode: function() 
25249     {
25250         // this may only work on Gecko!!!
25251         
25252         // should we cache this!!!!
25253         
25254         
25255         
25256          
25257         var range = this.createRange(this.getSelection()).cloneRange();
25258         
25259         if (Roo.isIE) {
25260             var parent = range.parentElement();
25261             while (true) {
25262                 var testRange = range.duplicate();
25263                 testRange.moveToElementText(parent);
25264                 if (testRange.inRange(range)) {
25265                     break;
25266                 }
25267                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25268                     break;
25269                 }
25270                 parent = parent.parentElement;
25271             }
25272             return parent;
25273         }
25274         
25275         // is ancestor a text element.
25276         var ac =  range.commonAncestorContainer;
25277         if (ac.nodeType == 3) {
25278             ac = ac.parentNode;
25279         }
25280         
25281         var ar = ac.childNodes;
25282          
25283         var nodes = [];
25284         var other_nodes = [];
25285         var has_other_nodes = false;
25286         for (var i=0;i<ar.length;i++) {
25287             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25288                 continue;
25289             }
25290             // fullly contained node.
25291             
25292             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25293                 nodes.push(ar[i]);
25294                 continue;
25295             }
25296             
25297             // probably selected..
25298             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25299                 other_nodes.push(ar[i]);
25300                 continue;
25301             }
25302             // outer..
25303             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25304                 continue;
25305             }
25306             
25307             
25308             has_other_nodes = true;
25309         }
25310         if (!nodes.length && other_nodes.length) {
25311             nodes= other_nodes;
25312         }
25313         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25314             return false;
25315         }
25316         
25317         return nodes[0];
25318     },
25319     createRange: function(sel)
25320     {
25321         // this has strange effects when using with 
25322         // top toolbar - not sure if it's a great idea.
25323         //this.editor.contentWindow.focus();
25324         if (typeof sel != "undefined") {
25325             try {
25326                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25327             } catch(e) {
25328                 return this.doc.createRange();
25329             }
25330         } else {
25331             return this.doc.createRange();
25332         }
25333     },
25334     getParentElement: function()
25335     {
25336         
25337         this.assignDocWin();
25338         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25339         
25340         var range = this.createRange(sel);
25341          
25342         try {
25343             var p = range.commonAncestorContainer;
25344             while (p.nodeType == 3) { // text node
25345                 p = p.parentNode;
25346             }
25347             return p;
25348         } catch (e) {
25349             return null;
25350         }
25351     
25352     },
25353     /***
25354      *
25355      * Range intersection.. the hard stuff...
25356      *  '-1' = before
25357      *  '0' = hits..
25358      *  '1' = after.
25359      *         [ -- selected range --- ]
25360      *   [fail]                        [fail]
25361      *
25362      *    basically..
25363      *      if end is before start or  hits it. fail.
25364      *      if start is after end or hits it fail.
25365      *
25366      *   if either hits (but other is outside. - then it's not 
25367      *   
25368      *    
25369      **/
25370     
25371     
25372     // @see http://www.thismuchiknow.co.uk/?p=64.
25373     rangeIntersectsNode : function(range, node)
25374     {
25375         var nodeRange = node.ownerDocument.createRange();
25376         try {
25377             nodeRange.selectNode(node);
25378         } catch (e) {
25379             nodeRange.selectNodeContents(node);
25380         }
25381     
25382         var rangeStartRange = range.cloneRange();
25383         rangeStartRange.collapse(true);
25384     
25385         var rangeEndRange = range.cloneRange();
25386         rangeEndRange.collapse(false);
25387     
25388         var nodeStartRange = nodeRange.cloneRange();
25389         nodeStartRange.collapse(true);
25390     
25391         var nodeEndRange = nodeRange.cloneRange();
25392         nodeEndRange.collapse(false);
25393     
25394         return rangeStartRange.compareBoundaryPoints(
25395                  Range.START_TO_START, nodeEndRange) == -1 &&
25396                rangeEndRange.compareBoundaryPoints(
25397                  Range.START_TO_START, nodeStartRange) == 1;
25398         
25399          
25400     },
25401     rangeCompareNode : function(range, node)
25402     {
25403         var nodeRange = node.ownerDocument.createRange();
25404         try {
25405             nodeRange.selectNode(node);
25406         } catch (e) {
25407             nodeRange.selectNodeContents(node);
25408         }
25409         
25410         
25411         range.collapse(true);
25412     
25413         nodeRange.collapse(true);
25414      
25415         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25416         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25417          
25418         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25419         
25420         var nodeIsBefore   =  ss == 1;
25421         var nodeIsAfter    = ee == -1;
25422         
25423         if (nodeIsBefore && nodeIsAfter) {
25424             return 0; // outer
25425         }
25426         if (!nodeIsBefore && nodeIsAfter) {
25427             return 1; //right trailed.
25428         }
25429         
25430         if (nodeIsBefore && !nodeIsAfter) {
25431             return 2;  // left trailed.
25432         }
25433         // fully contined.
25434         return 3;
25435     },
25436
25437     // private? - in a new class?
25438     cleanUpPaste :  function()
25439     {
25440         // cleans up the whole document..
25441         Roo.log('cleanuppaste');
25442         
25443         this.cleanUpChildren(this.doc.body);
25444         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25445         if (clean != this.doc.body.innerHTML) {
25446             this.doc.body.innerHTML = clean;
25447         }
25448         
25449     },
25450     
25451     cleanWordChars : function(input) {// change the chars to hex code
25452         var he = Roo.HtmlEditorCore;
25453         
25454         var output = input;
25455         Roo.each(he.swapCodes, function(sw) { 
25456             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25457             
25458             output = output.replace(swapper, sw[1]);
25459         });
25460         
25461         return output;
25462     },
25463     
25464     
25465     cleanUpChildren : function (n)
25466     {
25467         if (!n.childNodes.length) {
25468             return;
25469         }
25470         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25471            this.cleanUpChild(n.childNodes[i]);
25472         }
25473     },
25474     
25475     
25476         
25477     
25478     cleanUpChild : function (node)
25479     {
25480         var ed = this;
25481         //console.log(node);
25482         if (node.nodeName == "#text") {
25483             // clean up silly Windows -- stuff?
25484             return; 
25485         }
25486         if (node.nodeName == "#comment") {
25487             node.parentNode.removeChild(node);
25488             // clean up silly Windows -- stuff?
25489             return; 
25490         }
25491         var lcname = node.tagName.toLowerCase();
25492         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25493         // whitelist of tags..
25494         
25495         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25496             // remove node.
25497             node.parentNode.removeChild(node);
25498             return;
25499             
25500         }
25501         
25502         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25503         
25504         // spans with no attributes - just remove them..
25505         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25506             remove_keep_children = true;
25507         }
25508         
25509         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25510         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25511         
25512         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25513         //    remove_keep_children = true;
25514         //}
25515         
25516         if (remove_keep_children) {
25517             this.cleanUpChildren(node);
25518             // inserts everything just before this node...
25519             while (node.childNodes.length) {
25520                 var cn = node.childNodes[0];
25521                 node.removeChild(cn);
25522                 node.parentNode.insertBefore(cn, node);
25523             }
25524             node.parentNode.removeChild(node);
25525             return;
25526         }
25527         
25528         if (!node.attributes || !node.attributes.length) {
25529             
25530           
25531             
25532             
25533             this.cleanUpChildren(node);
25534             return;
25535         }
25536         
25537         function cleanAttr(n,v)
25538         {
25539             
25540             if (v.match(/^\./) || v.match(/^\//)) {
25541                 return;
25542             }
25543             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25544                 return;
25545             }
25546             if (v.match(/^#/)) {
25547                 return;
25548             }
25549             if (v.match(/^\{/)) { // allow template editing.
25550                 return;
25551             }
25552 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25553             node.removeAttribute(n);
25554             
25555         }
25556         
25557         var cwhite = this.cwhite;
25558         var cblack = this.cblack;
25559             
25560         function cleanStyle(n,v)
25561         {
25562             if (v.match(/expression/)) { //XSS?? should we even bother..
25563                 node.removeAttribute(n);
25564                 return;
25565             }
25566             
25567             var parts = v.split(/;/);
25568             var clean = [];
25569             
25570             Roo.each(parts, function(p) {
25571                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25572                 if (!p.length) {
25573                     return true;
25574                 }
25575                 var l = p.split(':').shift().replace(/\s+/g,'');
25576                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25577                 
25578                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25579 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25580                     //node.removeAttribute(n);
25581                     return true;
25582                 }
25583                 //Roo.log()
25584                 // only allow 'c whitelisted system attributes'
25585                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25586 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25587                     //node.removeAttribute(n);
25588                     return true;
25589                 }
25590                 
25591                 
25592                  
25593                 
25594                 clean.push(p);
25595                 return true;
25596             });
25597             if (clean.length) { 
25598                 node.setAttribute(n, clean.join(';'));
25599             } else {
25600                 node.removeAttribute(n);
25601             }
25602             
25603         }
25604         
25605         
25606         for (var i = node.attributes.length-1; i > -1 ; i--) {
25607             var a = node.attributes[i];
25608             //console.log(a);
25609             
25610             if (a.name.toLowerCase().substr(0,2)=='on')  {
25611                 node.removeAttribute(a.name);
25612                 continue;
25613             }
25614             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25615                 node.removeAttribute(a.name);
25616                 continue;
25617             }
25618             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25619                 cleanAttr(a.name,a.value); // fixme..
25620                 continue;
25621             }
25622             if (a.name == 'style') {
25623                 cleanStyle(a.name,a.value);
25624                 continue;
25625             }
25626             /// clean up MS crap..
25627             // tecnically this should be a list of valid class'es..
25628             
25629             
25630             if (a.name == 'class') {
25631                 if (a.value.match(/^Mso/)) {
25632                     node.removeAttribute('class');
25633                 }
25634                 
25635                 if (a.value.match(/^body$/)) {
25636                     node.removeAttribute('class');
25637                 }
25638                 continue;
25639             }
25640             
25641             // style cleanup!?
25642             // class cleanup?
25643             
25644         }
25645         
25646         
25647         this.cleanUpChildren(node);
25648         
25649         
25650     },
25651     
25652     /**
25653      * Clean up MS wordisms...
25654      */
25655     cleanWord : function(node)
25656     {
25657         if (!node) {
25658             this.cleanWord(this.doc.body);
25659             return;
25660         }
25661         
25662         if(
25663                 node.nodeName == 'SPAN' &&
25664                 !node.hasAttributes() &&
25665                 node.childNodes.length == 1 &&
25666                 node.firstChild.nodeName == "#text"  
25667         ) {
25668             var textNode = node.firstChild;
25669             node.removeChild(textNode);
25670             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25671                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25672             }
25673             node.parentNode.insertBefore(textNode, node);
25674             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25675                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25676             }
25677             node.parentNode.removeChild(node);
25678         }
25679         
25680         if (node.nodeName == "#text") {
25681             // clean up silly Windows -- stuff?
25682             return; 
25683         }
25684         if (node.nodeName == "#comment") {
25685             node.parentNode.removeChild(node);
25686             // clean up silly Windows -- stuff?
25687             return; 
25688         }
25689         
25690         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25691             node.parentNode.removeChild(node);
25692             return;
25693         }
25694         //Roo.log(node.tagName);
25695         // remove - but keep children..
25696         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25697             //Roo.log('-- removed');
25698             while (node.childNodes.length) {
25699                 var cn = node.childNodes[0];
25700                 node.removeChild(cn);
25701                 node.parentNode.insertBefore(cn, node);
25702                 // move node to parent - and clean it..
25703                 this.cleanWord(cn);
25704             }
25705             node.parentNode.removeChild(node);
25706             /// no need to iterate chidlren = it's got none..
25707             //this.iterateChildren(node, this.cleanWord);
25708             return;
25709         }
25710         // clean styles
25711         if (node.className.length) {
25712             
25713             var cn = node.className.split(/\W+/);
25714             var cna = [];
25715             Roo.each(cn, function(cls) {
25716                 if (cls.match(/Mso[a-zA-Z]+/)) {
25717                     return;
25718                 }
25719                 cna.push(cls);
25720             });
25721             node.className = cna.length ? cna.join(' ') : '';
25722             if (!cna.length) {
25723                 node.removeAttribute("class");
25724             }
25725         }
25726         
25727         if (node.hasAttribute("lang")) {
25728             node.removeAttribute("lang");
25729         }
25730         
25731         if (node.hasAttribute("style")) {
25732             
25733             var styles = node.getAttribute("style").split(";");
25734             var nstyle = [];
25735             Roo.each(styles, function(s) {
25736                 if (!s.match(/:/)) {
25737                     return;
25738                 }
25739                 var kv = s.split(":");
25740                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25741                     return;
25742                 }
25743                 // what ever is left... we allow.
25744                 nstyle.push(s);
25745             });
25746             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25747             if (!nstyle.length) {
25748                 node.removeAttribute('style');
25749             }
25750         }
25751         this.iterateChildren(node, this.cleanWord);
25752         
25753         
25754         
25755     },
25756     /**
25757      * iterateChildren of a Node, calling fn each time, using this as the scole..
25758      * @param {DomNode} node node to iterate children of.
25759      * @param {Function} fn method of this class to call on each item.
25760      */
25761     iterateChildren : function(node, fn)
25762     {
25763         if (!node.childNodes.length) {
25764                 return;
25765         }
25766         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25767            fn.call(this, node.childNodes[i])
25768         }
25769     },
25770     
25771     
25772     /**
25773      * cleanTableWidths.
25774      *
25775      * Quite often pasting from word etc.. results in tables with column and widths.
25776      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25777      *
25778      */
25779     cleanTableWidths : function(node)
25780     {
25781          
25782          
25783         if (!node) {
25784             this.cleanTableWidths(this.doc.body);
25785             return;
25786         }
25787         
25788         // ignore list...
25789         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25790             return; 
25791         }
25792         Roo.log(node.tagName);
25793         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25794             this.iterateChildren(node, this.cleanTableWidths);
25795             return;
25796         }
25797         if (node.hasAttribute('width')) {
25798             node.removeAttribute('width');
25799         }
25800         
25801          
25802         if (node.hasAttribute("style")) {
25803             // pretty basic...
25804             
25805             var styles = node.getAttribute("style").split(";");
25806             var nstyle = [];
25807             Roo.each(styles, function(s) {
25808                 if (!s.match(/:/)) {
25809                     return;
25810                 }
25811                 var kv = s.split(":");
25812                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25813                     return;
25814                 }
25815                 // what ever is left... we allow.
25816                 nstyle.push(s);
25817             });
25818             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25819             if (!nstyle.length) {
25820                 node.removeAttribute('style');
25821             }
25822         }
25823         
25824         this.iterateChildren(node, this.cleanTableWidths);
25825         
25826         
25827     },
25828     
25829     
25830     
25831     
25832     domToHTML : function(currentElement, depth, nopadtext) {
25833         
25834         depth = depth || 0;
25835         nopadtext = nopadtext || false;
25836     
25837         if (!currentElement) {
25838             return this.domToHTML(this.doc.body);
25839         }
25840         
25841         //Roo.log(currentElement);
25842         var j;
25843         var allText = false;
25844         var nodeName = currentElement.nodeName;
25845         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25846         
25847         if  (nodeName == '#text') {
25848             
25849             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25850         }
25851         
25852         
25853         var ret = '';
25854         if (nodeName != 'BODY') {
25855              
25856             var i = 0;
25857             // Prints the node tagName, such as <A>, <IMG>, etc
25858             if (tagName) {
25859                 var attr = [];
25860                 for(i = 0; i < currentElement.attributes.length;i++) {
25861                     // quoting?
25862                     var aname = currentElement.attributes.item(i).name;
25863                     if (!currentElement.attributes.item(i).value.length) {
25864                         continue;
25865                     }
25866                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25867                 }
25868                 
25869                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25870             } 
25871             else {
25872                 
25873                 // eack
25874             }
25875         } else {
25876             tagName = false;
25877         }
25878         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25879             return ret;
25880         }
25881         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25882             nopadtext = true;
25883         }
25884         
25885         
25886         // Traverse the tree
25887         i = 0;
25888         var currentElementChild = currentElement.childNodes.item(i);
25889         var allText = true;
25890         var innerHTML  = '';
25891         lastnode = '';
25892         while (currentElementChild) {
25893             // Formatting code (indent the tree so it looks nice on the screen)
25894             var nopad = nopadtext;
25895             if (lastnode == 'SPAN') {
25896                 nopad  = true;
25897             }
25898             // text
25899             if  (currentElementChild.nodeName == '#text') {
25900                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25901                 toadd = nopadtext ? toadd : toadd.trim();
25902                 if (!nopad && toadd.length > 80) {
25903                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25904                 }
25905                 innerHTML  += toadd;
25906                 
25907                 i++;
25908                 currentElementChild = currentElement.childNodes.item(i);
25909                 lastNode = '';
25910                 continue;
25911             }
25912             allText = false;
25913             
25914             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25915                 
25916             // Recursively traverse the tree structure of the child node
25917             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25918             lastnode = currentElementChild.nodeName;
25919             i++;
25920             currentElementChild=currentElement.childNodes.item(i);
25921         }
25922         
25923         ret += innerHTML;
25924         
25925         if (!allText) {
25926                 // The remaining code is mostly for formatting the tree
25927             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25928         }
25929         
25930         
25931         if (tagName) {
25932             ret+= "</"+tagName+">";
25933         }
25934         return ret;
25935         
25936     },
25937         
25938     applyBlacklists : function()
25939     {
25940         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25941         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25942         
25943         this.white = [];
25944         this.black = [];
25945         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25946             if (b.indexOf(tag) > -1) {
25947                 return;
25948             }
25949             this.white.push(tag);
25950             
25951         }, this);
25952         
25953         Roo.each(w, function(tag) {
25954             if (b.indexOf(tag) > -1) {
25955                 return;
25956             }
25957             if (this.white.indexOf(tag) > -1) {
25958                 return;
25959             }
25960             this.white.push(tag);
25961             
25962         }, this);
25963         
25964         
25965         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25966             if (w.indexOf(tag) > -1) {
25967                 return;
25968             }
25969             this.black.push(tag);
25970             
25971         }, this);
25972         
25973         Roo.each(b, function(tag) {
25974             if (w.indexOf(tag) > -1) {
25975                 return;
25976             }
25977             if (this.black.indexOf(tag) > -1) {
25978                 return;
25979             }
25980             this.black.push(tag);
25981             
25982         }, this);
25983         
25984         
25985         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25986         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25987         
25988         this.cwhite = [];
25989         this.cblack = [];
25990         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25991             if (b.indexOf(tag) > -1) {
25992                 return;
25993             }
25994             this.cwhite.push(tag);
25995             
25996         }, this);
25997         
25998         Roo.each(w, function(tag) {
25999             if (b.indexOf(tag) > -1) {
26000                 return;
26001             }
26002             if (this.cwhite.indexOf(tag) > -1) {
26003                 return;
26004             }
26005             this.cwhite.push(tag);
26006             
26007         }, this);
26008         
26009         
26010         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26011             if (w.indexOf(tag) > -1) {
26012                 return;
26013             }
26014             this.cblack.push(tag);
26015             
26016         }, this);
26017         
26018         Roo.each(b, function(tag) {
26019             if (w.indexOf(tag) > -1) {
26020                 return;
26021             }
26022             if (this.cblack.indexOf(tag) > -1) {
26023                 return;
26024             }
26025             this.cblack.push(tag);
26026             
26027         }, this);
26028     },
26029     
26030     setStylesheets : function(stylesheets)
26031     {
26032         if(typeof(stylesheets) == 'string'){
26033             Roo.get(this.iframe.contentDocument.head).createChild({
26034                 tag : 'link',
26035                 rel : 'stylesheet',
26036                 type : 'text/css',
26037                 href : stylesheets
26038             });
26039             
26040             return;
26041         }
26042         var _this = this;
26043      
26044         Roo.each(stylesheets, function(s) {
26045             if(!s.length){
26046                 return;
26047             }
26048             
26049             Roo.get(_this.iframe.contentDocument.head).createChild({
26050                 tag : 'link',
26051                 rel : 'stylesheet',
26052                 type : 'text/css',
26053                 href : s
26054             });
26055         });
26056
26057         
26058     },
26059     
26060     removeStylesheets : function()
26061     {
26062         var _this = this;
26063         
26064         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26065             s.remove();
26066         });
26067     },
26068     
26069     setStyle : function(style)
26070     {
26071         Roo.get(this.iframe.contentDocument.head).createChild({
26072             tag : 'style',
26073             type : 'text/css',
26074             html : style
26075         });
26076
26077         return;
26078     }
26079     
26080     // hide stuff that is not compatible
26081     /**
26082      * @event blur
26083      * @hide
26084      */
26085     /**
26086      * @event change
26087      * @hide
26088      */
26089     /**
26090      * @event focus
26091      * @hide
26092      */
26093     /**
26094      * @event specialkey
26095      * @hide
26096      */
26097     /**
26098      * @cfg {String} fieldClass @hide
26099      */
26100     /**
26101      * @cfg {String} focusClass @hide
26102      */
26103     /**
26104      * @cfg {String} autoCreate @hide
26105      */
26106     /**
26107      * @cfg {String} inputType @hide
26108      */
26109     /**
26110      * @cfg {String} invalidClass @hide
26111      */
26112     /**
26113      * @cfg {String} invalidText @hide
26114      */
26115     /**
26116      * @cfg {String} msgFx @hide
26117      */
26118     /**
26119      * @cfg {String} validateOnBlur @hide
26120      */
26121 });
26122
26123 Roo.HtmlEditorCore.white = [
26124         'area', 'br', 'img', 'input', 'hr', 'wbr',
26125         
26126        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26127        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26128        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26129        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26130        'table',   'ul',         'xmp', 
26131        
26132        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26133       'thead',   'tr', 
26134      
26135       'dir', 'menu', 'ol', 'ul', 'dl',
26136        
26137       'embed',  'object'
26138 ];
26139
26140
26141 Roo.HtmlEditorCore.black = [
26142     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26143         'applet', // 
26144         'base',   'basefont', 'bgsound', 'blink',  'body', 
26145         'frame',  'frameset', 'head',    'html',   'ilayer', 
26146         'iframe', 'layer',  'link',     'meta',    'object',   
26147         'script', 'style' ,'title',  'xml' // clean later..
26148 ];
26149 Roo.HtmlEditorCore.clean = [
26150     'script', 'style', 'title', 'xml'
26151 ];
26152 Roo.HtmlEditorCore.remove = [
26153     'font'
26154 ];
26155 // attributes..
26156
26157 Roo.HtmlEditorCore.ablack = [
26158     'on'
26159 ];
26160     
26161 Roo.HtmlEditorCore.aclean = [ 
26162     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26163 ];
26164
26165 // protocols..
26166 Roo.HtmlEditorCore.pwhite= [
26167         'http',  'https',  'mailto'
26168 ];
26169
26170 // white listed style attributes.
26171 Roo.HtmlEditorCore.cwhite= [
26172       //  'text-align', /// default is to allow most things..
26173       
26174          
26175 //        'font-size'//??
26176 ];
26177
26178 // black listed style attributes.
26179 Roo.HtmlEditorCore.cblack= [
26180       //  'font-size' -- this can be set by the project 
26181 ];
26182
26183
26184 Roo.HtmlEditorCore.swapCodes   =[ 
26185     [    8211, "&#8211;" ], 
26186     [    8212, "&#8212;" ], 
26187     [    8216,  "'" ],  
26188     [    8217, "'" ],  
26189     [    8220, '"' ],  
26190     [    8221, '"' ],  
26191     [    8226, "*" ],  
26192     [    8230, "..." ]
26193 ]; 
26194
26195     /*
26196  * - LGPL
26197  *
26198  * HtmlEditor
26199  * 
26200  */
26201
26202 /**
26203  * @class Roo.bootstrap.HtmlEditor
26204  * @extends Roo.bootstrap.TextArea
26205  * Bootstrap HtmlEditor class
26206
26207  * @constructor
26208  * Create a new HtmlEditor
26209  * @param {Object} config The config object
26210  */
26211
26212 Roo.bootstrap.HtmlEditor = function(config){
26213     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26214     if (!this.toolbars) {
26215         this.toolbars = [];
26216     }
26217     
26218     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26219     this.addEvents({
26220             /**
26221              * @event initialize
26222              * Fires when the editor is fully initialized (including the iframe)
26223              * @param {HtmlEditor} this
26224              */
26225             initialize: true,
26226             /**
26227              * @event activate
26228              * Fires when the editor is first receives the focus. Any insertion must wait
26229              * until after this event.
26230              * @param {HtmlEditor} this
26231              */
26232             activate: true,
26233              /**
26234              * @event beforesync
26235              * Fires before the textarea is updated with content from the editor iframe. Return false
26236              * to cancel the sync.
26237              * @param {HtmlEditor} this
26238              * @param {String} html
26239              */
26240             beforesync: true,
26241              /**
26242              * @event beforepush
26243              * Fires before the iframe editor is updated with content from the textarea. Return false
26244              * to cancel the push.
26245              * @param {HtmlEditor} this
26246              * @param {String} html
26247              */
26248             beforepush: true,
26249              /**
26250              * @event sync
26251              * Fires when the textarea is updated with content from the editor iframe.
26252              * @param {HtmlEditor} this
26253              * @param {String} html
26254              */
26255             sync: true,
26256              /**
26257              * @event push
26258              * Fires when the iframe editor is updated with content from the textarea.
26259              * @param {HtmlEditor} this
26260              * @param {String} html
26261              */
26262             push: true,
26263              /**
26264              * @event editmodechange
26265              * Fires when the editor switches edit modes
26266              * @param {HtmlEditor} this
26267              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26268              */
26269             editmodechange: true,
26270             /**
26271              * @event editorevent
26272              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26273              * @param {HtmlEditor} this
26274              */
26275             editorevent: true,
26276             /**
26277              * @event firstfocus
26278              * Fires when on first focus - needed by toolbars..
26279              * @param {HtmlEditor} this
26280              */
26281             firstfocus: true,
26282             /**
26283              * @event autosave
26284              * Auto save the htmlEditor value as a file into Events
26285              * @param {HtmlEditor} this
26286              */
26287             autosave: true,
26288             /**
26289              * @event savedpreview
26290              * preview the saved version of htmlEditor
26291              * @param {HtmlEditor} this
26292              */
26293             savedpreview: true
26294         });
26295 };
26296
26297
26298 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26299     
26300     
26301       /**
26302      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26303      */
26304     toolbars : false,
26305     
26306      /**
26307     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26308     */
26309     btns : [],
26310    
26311      /**
26312      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26313      *                        Roo.resizable.
26314      */
26315     resizable : false,
26316      /**
26317      * @cfg {Number} height (in pixels)
26318      */   
26319     height: 300,
26320    /**
26321      * @cfg {Number} width (in pixels)
26322      */   
26323     width: false,
26324     
26325     /**
26326      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26327      * 
26328      */
26329     stylesheets: false,
26330     
26331     // id of frame..
26332     frameId: false,
26333     
26334     // private properties
26335     validationEvent : false,
26336     deferHeight: true,
26337     initialized : false,
26338     activated : false,
26339     
26340     onFocus : Roo.emptyFn,
26341     iframePad:3,
26342     hideMode:'offsets',
26343     
26344     tbContainer : false,
26345     
26346     bodyCls : '',
26347     
26348     toolbarContainer :function() {
26349         return this.wrap.select('.x-html-editor-tb',true).first();
26350     },
26351
26352     /**
26353      * Protected method that will not generally be called directly. It
26354      * is called when the editor creates its toolbar. Override this method if you need to
26355      * add custom toolbar buttons.
26356      * @param {HtmlEditor} editor
26357      */
26358     createToolbar : function(){
26359         Roo.log('renewing');
26360         Roo.log("create toolbars");
26361         
26362         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26363         this.toolbars[0].render(this.toolbarContainer());
26364         
26365         return;
26366         
26367 //        if (!editor.toolbars || !editor.toolbars.length) {
26368 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26369 //        }
26370 //        
26371 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26372 //            editor.toolbars[i] = Roo.factory(
26373 //                    typeof(editor.toolbars[i]) == 'string' ?
26374 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26375 //                Roo.bootstrap.HtmlEditor);
26376 //            editor.toolbars[i].init(editor);
26377 //        }
26378     },
26379
26380      
26381     // private
26382     onRender : function(ct, position)
26383     {
26384        // Roo.log("Call onRender: " + this.xtype);
26385         var _t = this;
26386         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26387       
26388         this.wrap = this.inputEl().wrap({
26389             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26390         });
26391         
26392         this.editorcore.onRender(ct, position);
26393          
26394         if (this.resizable) {
26395             this.resizeEl = new Roo.Resizable(this.wrap, {
26396                 pinned : true,
26397                 wrap: true,
26398                 dynamic : true,
26399                 minHeight : this.height,
26400                 height: this.height,
26401                 handles : this.resizable,
26402                 width: this.width,
26403                 listeners : {
26404                     resize : function(r, w, h) {
26405                         _t.onResize(w,h); // -something
26406                     }
26407                 }
26408             });
26409             
26410         }
26411         this.createToolbar(this);
26412        
26413         
26414         if(!this.width && this.resizable){
26415             this.setSize(this.wrap.getSize());
26416         }
26417         if (this.resizeEl) {
26418             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26419             // should trigger onReize..
26420         }
26421         
26422     },
26423
26424     // private
26425     onResize : function(w, h)
26426     {
26427         Roo.log('resize: ' +w + ',' + h );
26428         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26429         var ew = false;
26430         var eh = false;
26431         
26432         if(this.inputEl() ){
26433             if(typeof w == 'number'){
26434                 var aw = w - this.wrap.getFrameWidth('lr');
26435                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26436                 ew = aw;
26437             }
26438             if(typeof h == 'number'){
26439                  var tbh = -11;  // fixme it needs to tool bar size!
26440                 for (var i =0; i < this.toolbars.length;i++) {
26441                     // fixme - ask toolbars for heights?
26442                     tbh += this.toolbars[i].el.getHeight();
26443                     //if (this.toolbars[i].footer) {
26444                     //    tbh += this.toolbars[i].footer.el.getHeight();
26445                     //}
26446                 }
26447               
26448                 
26449                 
26450                 
26451                 
26452                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26453                 ah -= 5; // knock a few pixes off for look..
26454                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26455                 var eh = ah;
26456             }
26457         }
26458         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26459         this.editorcore.onResize(ew,eh);
26460         
26461     },
26462
26463     /**
26464      * Toggles the editor between standard and source edit mode.
26465      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26466      */
26467     toggleSourceEdit : function(sourceEditMode)
26468     {
26469         this.editorcore.toggleSourceEdit(sourceEditMode);
26470         
26471         if(this.editorcore.sourceEditMode){
26472             Roo.log('editor - showing textarea');
26473             
26474 //            Roo.log('in');
26475 //            Roo.log(this.syncValue());
26476             this.syncValue();
26477             this.inputEl().removeClass(['hide', 'x-hidden']);
26478             this.inputEl().dom.removeAttribute('tabIndex');
26479             this.inputEl().focus();
26480         }else{
26481             Roo.log('editor - hiding textarea');
26482 //            Roo.log('out')
26483 //            Roo.log(this.pushValue()); 
26484             this.pushValue();
26485             
26486             this.inputEl().addClass(['hide', 'x-hidden']);
26487             this.inputEl().dom.setAttribute('tabIndex', -1);
26488             //this.deferFocus();
26489         }
26490          
26491         if(this.resizable){
26492             this.setSize(this.wrap.getSize());
26493         }
26494         
26495         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26496     },
26497  
26498     // private (for BoxComponent)
26499     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26500
26501     // private (for BoxComponent)
26502     getResizeEl : function(){
26503         return this.wrap;
26504     },
26505
26506     // private (for BoxComponent)
26507     getPositionEl : function(){
26508         return this.wrap;
26509     },
26510
26511     // private
26512     initEvents : function(){
26513         this.originalValue = this.getValue();
26514     },
26515
26516 //    /**
26517 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26518 //     * @method
26519 //     */
26520 //    markInvalid : Roo.emptyFn,
26521 //    /**
26522 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26523 //     * @method
26524 //     */
26525 //    clearInvalid : Roo.emptyFn,
26526
26527     setValue : function(v){
26528         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26529         this.editorcore.pushValue();
26530     },
26531
26532      
26533     // private
26534     deferFocus : function(){
26535         this.focus.defer(10, this);
26536     },
26537
26538     // doc'ed in Field
26539     focus : function(){
26540         this.editorcore.focus();
26541         
26542     },
26543       
26544
26545     // private
26546     onDestroy : function(){
26547         
26548         
26549         
26550         if(this.rendered){
26551             
26552             for (var i =0; i < this.toolbars.length;i++) {
26553                 // fixme - ask toolbars for heights?
26554                 this.toolbars[i].onDestroy();
26555             }
26556             
26557             this.wrap.dom.innerHTML = '';
26558             this.wrap.remove();
26559         }
26560     },
26561
26562     // private
26563     onFirstFocus : function(){
26564         //Roo.log("onFirstFocus");
26565         this.editorcore.onFirstFocus();
26566          for (var i =0; i < this.toolbars.length;i++) {
26567             this.toolbars[i].onFirstFocus();
26568         }
26569         
26570     },
26571     
26572     // private
26573     syncValue : function()
26574     {   
26575         this.editorcore.syncValue();
26576     },
26577     
26578     pushValue : function()
26579     {   
26580         this.editorcore.pushValue();
26581     }
26582      
26583     
26584     // hide stuff that is not compatible
26585     /**
26586      * @event blur
26587      * @hide
26588      */
26589     /**
26590      * @event change
26591      * @hide
26592      */
26593     /**
26594      * @event focus
26595      * @hide
26596      */
26597     /**
26598      * @event specialkey
26599      * @hide
26600      */
26601     /**
26602      * @cfg {String} fieldClass @hide
26603      */
26604     /**
26605      * @cfg {String} focusClass @hide
26606      */
26607     /**
26608      * @cfg {String} autoCreate @hide
26609      */
26610     /**
26611      * @cfg {String} inputType @hide
26612      */
26613      
26614     /**
26615      * @cfg {String} invalidText @hide
26616      */
26617     /**
26618      * @cfg {String} msgFx @hide
26619      */
26620     /**
26621      * @cfg {String} validateOnBlur @hide
26622      */
26623 });
26624  
26625     
26626    
26627    
26628    
26629       
26630 Roo.namespace('Roo.bootstrap.htmleditor');
26631 /**
26632  * @class Roo.bootstrap.HtmlEditorToolbar1
26633  * Basic Toolbar
26634  * 
26635  * @example
26636  * Usage:
26637  *
26638  new Roo.bootstrap.HtmlEditor({
26639     ....
26640     toolbars : [
26641         new Roo.bootstrap.HtmlEditorToolbar1({
26642             disable : { fonts: 1 , format: 1, ..., ... , ...],
26643             btns : [ .... ]
26644         })
26645     }
26646      
26647  * 
26648  * @cfg {Object} disable List of elements to disable..
26649  * @cfg {Array} btns List of additional buttons.
26650  * 
26651  * 
26652  * NEEDS Extra CSS? 
26653  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26654  */
26655  
26656 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26657 {
26658     
26659     Roo.apply(this, config);
26660     
26661     // default disabled, based on 'good practice'..
26662     this.disable = this.disable || {};
26663     Roo.applyIf(this.disable, {
26664         fontSize : true,
26665         colors : true,
26666         specialElements : true
26667     });
26668     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26669     
26670     this.editor = config.editor;
26671     this.editorcore = config.editor.editorcore;
26672     
26673     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26674     
26675     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26676     // dont call parent... till later.
26677 }
26678 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26679      
26680     bar : true,
26681     
26682     editor : false,
26683     editorcore : false,
26684     
26685     
26686     formats : [
26687         "p" ,  
26688         "h1","h2","h3","h4","h5","h6", 
26689         "pre", "code", 
26690         "abbr", "acronym", "address", "cite", "samp", "var",
26691         'div','span'
26692     ],
26693     
26694     onRender : function(ct, position)
26695     {
26696        // Roo.log("Call onRender: " + this.xtype);
26697         
26698        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26699        Roo.log(this.el);
26700        this.el.dom.style.marginBottom = '0';
26701        var _this = this;
26702        var editorcore = this.editorcore;
26703        var editor= this.editor;
26704        
26705        var children = [];
26706        var btn = function(id,cmd , toggle, handler, html){
26707        
26708             var  event = toggle ? 'toggle' : 'click';
26709        
26710             var a = {
26711                 size : 'sm',
26712                 xtype: 'Button',
26713                 xns: Roo.bootstrap,
26714                 //glyphicon : id,
26715                 fa: id,
26716                 cmd : id || cmd,
26717                 enableToggle:toggle !== false,
26718                 html : html || '',
26719                 pressed : toggle ? false : null,
26720                 listeners : {}
26721             };
26722             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26723                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26724             };
26725             children.push(a);
26726             return a;
26727        }
26728        
26729     //    var cb_box = function...
26730         
26731         var style = {
26732                 xtype: 'Button',
26733                 size : 'sm',
26734                 xns: Roo.bootstrap,
26735                 fa : 'font',
26736                 //html : 'submit'
26737                 menu : {
26738                     xtype: 'Menu',
26739                     xns: Roo.bootstrap,
26740                     items:  []
26741                 }
26742         };
26743         Roo.each(this.formats, function(f) {
26744             style.menu.items.push({
26745                 xtype :'MenuItem',
26746                 xns: Roo.bootstrap,
26747                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26748                 tagname : f,
26749                 listeners : {
26750                     click : function()
26751                     {
26752                         editorcore.insertTag(this.tagname);
26753                         editor.focus();
26754                     }
26755                 }
26756                 
26757             });
26758         });
26759         children.push(style);   
26760         
26761         btn('bold',false,true);
26762         btn('italic',false,true);
26763         btn('align-left', 'justifyleft',true);
26764         btn('align-center', 'justifycenter',true);
26765         btn('align-right' , 'justifyright',true);
26766         btn('link', false, false, function(btn) {
26767             //Roo.log("create link?");
26768             var url = prompt(this.createLinkText, this.defaultLinkValue);
26769             if(url && url != 'http:/'+'/'){
26770                 this.editorcore.relayCmd('createlink', url);
26771             }
26772         }),
26773         btn('list','insertunorderedlist',true);
26774         btn('pencil', false,true, function(btn){
26775                 Roo.log(this);
26776                 this.toggleSourceEdit(btn.pressed);
26777         });
26778         
26779         if (this.editor.btns.length > 0) {
26780             for (var i = 0; i<this.editor.btns.length; i++) {
26781                 children.push(this.editor.btns[i]);
26782             }
26783         }
26784         
26785         /*
26786         var cog = {
26787                 xtype: 'Button',
26788                 size : 'sm',
26789                 xns: Roo.bootstrap,
26790                 glyphicon : 'cog',
26791                 //html : 'submit'
26792                 menu : {
26793                     xtype: 'Menu',
26794                     xns: Roo.bootstrap,
26795                     items:  []
26796                 }
26797         };
26798         
26799         cog.menu.items.push({
26800             xtype :'MenuItem',
26801             xns: Roo.bootstrap,
26802             html : Clean styles,
26803             tagname : f,
26804             listeners : {
26805                 click : function()
26806                 {
26807                     editorcore.insertTag(this.tagname);
26808                     editor.focus();
26809                 }
26810             }
26811             
26812         });
26813        */
26814         
26815          
26816        this.xtype = 'NavSimplebar';
26817         
26818         for(var i=0;i< children.length;i++) {
26819             
26820             this.buttons.add(this.addxtypeChild(children[i]));
26821             
26822         }
26823         
26824         editor.on('editorevent', this.updateToolbar, this);
26825     },
26826     onBtnClick : function(id)
26827     {
26828        this.editorcore.relayCmd(id);
26829        this.editorcore.focus();
26830     },
26831     
26832     /**
26833      * Protected method that will not generally be called directly. It triggers
26834      * a toolbar update by reading the markup state of the current selection in the editor.
26835      */
26836     updateToolbar: function(){
26837
26838         if(!this.editorcore.activated){
26839             this.editor.onFirstFocus(); // is this neeed?
26840             return;
26841         }
26842
26843         var btns = this.buttons; 
26844         var doc = this.editorcore.doc;
26845         btns.get('bold').setActive(doc.queryCommandState('bold'));
26846         btns.get('italic').setActive(doc.queryCommandState('italic'));
26847         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26848         
26849         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26850         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26851         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26852         
26853         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26854         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26855          /*
26856         
26857         var ans = this.editorcore.getAllAncestors();
26858         if (this.formatCombo) {
26859             
26860             
26861             var store = this.formatCombo.store;
26862             this.formatCombo.setValue("");
26863             for (var i =0; i < ans.length;i++) {
26864                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26865                     // select it..
26866                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26867                     break;
26868                 }
26869             }
26870         }
26871         
26872         
26873         
26874         // hides menus... - so this cant be on a menu...
26875         Roo.bootstrap.MenuMgr.hideAll();
26876         */
26877         Roo.bootstrap.MenuMgr.hideAll();
26878         //this.editorsyncValue();
26879     },
26880     onFirstFocus: function() {
26881         this.buttons.each(function(item){
26882            item.enable();
26883         });
26884     },
26885     toggleSourceEdit : function(sourceEditMode){
26886         
26887           
26888         if(sourceEditMode){
26889             Roo.log("disabling buttons");
26890            this.buttons.each( function(item){
26891                 if(item.cmd != 'pencil'){
26892                     item.disable();
26893                 }
26894             });
26895           
26896         }else{
26897             Roo.log("enabling buttons");
26898             if(this.editorcore.initialized){
26899                 this.buttons.each( function(item){
26900                     item.enable();
26901                 });
26902             }
26903             
26904         }
26905         Roo.log("calling toggole on editor");
26906         // tell the editor that it's been pressed..
26907         this.editor.toggleSourceEdit(sourceEditMode);
26908        
26909     }
26910 });
26911
26912
26913
26914
26915  
26916 /*
26917  * - LGPL
26918  */
26919
26920 /**
26921  * @class Roo.bootstrap.Markdown
26922  * @extends Roo.bootstrap.TextArea
26923  * Bootstrap Showdown editable area
26924  * @cfg {string} content
26925  * 
26926  * @constructor
26927  * Create a new Showdown
26928  */
26929
26930 Roo.bootstrap.Markdown = function(config){
26931     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26932    
26933 };
26934
26935 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26936     
26937     editing :false,
26938     
26939     initEvents : function()
26940     {
26941         
26942         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26943         this.markdownEl = this.el.createChild({
26944             cls : 'roo-markdown-area'
26945         });
26946         this.inputEl().addClass('d-none');
26947         if (this.getValue() == '') {
26948             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26949             
26950         } else {
26951             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26952         }
26953         this.markdownEl.on('click', this.toggleTextEdit, this);
26954         this.on('blur', this.toggleTextEdit, this);
26955         this.on('specialkey', this.resizeTextArea, this);
26956     },
26957     
26958     toggleTextEdit : function()
26959     {
26960         var sh = this.markdownEl.getHeight();
26961         this.inputEl().addClass('d-none');
26962         this.markdownEl.addClass('d-none');
26963         if (!this.editing) {
26964             // show editor?
26965             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26966             this.inputEl().removeClass('d-none');
26967             this.inputEl().focus();
26968             this.editing = true;
26969             return;
26970         }
26971         // show showdown...
26972         this.updateMarkdown();
26973         this.markdownEl.removeClass('d-none');
26974         this.editing = false;
26975         return;
26976     },
26977     updateMarkdown : function()
26978     {
26979         if (this.getValue() == '') {
26980             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26981             return;
26982         }
26983  
26984         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26985     },
26986     
26987     resizeTextArea: function () {
26988         
26989         var sh = 100;
26990         Roo.log([sh, this.getValue().split("\n").length * 30]);
26991         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26992     },
26993     setValue : function(val)
26994     {
26995         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26996         if (!this.editing) {
26997             this.updateMarkdown();
26998         }
26999         
27000     },
27001     focus : function()
27002     {
27003         if (!this.editing) {
27004             this.toggleTextEdit();
27005         }
27006         
27007     }
27008
27009
27010 });
27011 /**
27012  * @class Roo.bootstrap.Table.AbstractSelectionModel
27013  * @extends Roo.util.Observable
27014  * Abstract base class for grid SelectionModels.  It provides the interface that should be
27015  * implemented by descendant classes.  This class should not be directly instantiated.
27016  * @constructor
27017  */
27018 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27019     this.locked = false;
27020     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27021 };
27022
27023
27024 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27025     /** @ignore Called by the grid automatically. Do not call directly. */
27026     init : function(grid){
27027         this.grid = grid;
27028         this.initEvents();
27029     },
27030
27031     /**
27032      * Locks the selections.
27033      */
27034     lock : function(){
27035         this.locked = true;
27036     },
27037
27038     /**
27039      * Unlocks the selections.
27040      */
27041     unlock : function(){
27042         this.locked = false;
27043     },
27044
27045     /**
27046      * Returns true if the selections are locked.
27047      * @return {Boolean}
27048      */
27049     isLocked : function(){
27050         return this.locked;
27051     },
27052     
27053     
27054     initEvents : function ()
27055     {
27056         
27057     }
27058 });
27059 /**
27060  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27061  * @class Roo.bootstrap.Table.RowSelectionModel
27062  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27063  * It supports multiple selections and keyboard selection/navigation. 
27064  * @constructor
27065  * @param {Object} config
27066  */
27067
27068 Roo.bootstrap.Table.RowSelectionModel = function(config){
27069     Roo.apply(this, config);
27070     this.selections = new Roo.util.MixedCollection(false, function(o){
27071         return o.id;
27072     });
27073
27074     this.last = false;
27075     this.lastActive = false;
27076
27077     this.addEvents({
27078         /**
27079              * @event selectionchange
27080              * Fires when the selection changes
27081              * @param {SelectionModel} this
27082              */
27083             "selectionchange" : true,
27084         /**
27085              * @event afterselectionchange
27086              * Fires after the selection changes (eg. by key press or clicking)
27087              * @param {SelectionModel} this
27088              */
27089             "afterselectionchange" : true,
27090         /**
27091              * @event beforerowselect
27092              * Fires when a row is selected being selected, return false to cancel.
27093              * @param {SelectionModel} this
27094              * @param {Number} rowIndex The selected index
27095              * @param {Boolean} keepExisting False if other selections will be cleared
27096              */
27097             "beforerowselect" : true,
27098         /**
27099              * @event rowselect
27100              * Fires when a row is selected.
27101              * @param {SelectionModel} this
27102              * @param {Number} rowIndex The selected index
27103              * @param {Roo.data.Record} r The record
27104              */
27105             "rowselect" : true,
27106         /**
27107              * @event rowdeselect
27108              * Fires when a row is deselected.
27109              * @param {SelectionModel} this
27110              * @param {Number} rowIndex The selected index
27111              */
27112         "rowdeselect" : true
27113     });
27114     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27115     this.locked = false;
27116  };
27117
27118 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27119     /**
27120      * @cfg {Boolean} singleSelect
27121      * True to allow selection of only one row at a time (defaults to false)
27122      */
27123     singleSelect : false,
27124
27125     // private
27126     initEvents : function()
27127     {
27128
27129         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27130         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27131         //}else{ // allow click to work like normal
27132          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27133         //}
27134         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27135         this.grid.on("rowclick", this.handleMouseDown, this);
27136         
27137         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27138             "up" : function(e){
27139                 if(!e.shiftKey){
27140                     this.selectPrevious(e.shiftKey);
27141                 }else if(this.last !== false && this.lastActive !== false){
27142                     var last = this.last;
27143                     this.selectRange(this.last,  this.lastActive-1);
27144                     this.grid.getView().focusRow(this.lastActive);
27145                     if(last !== false){
27146                         this.last = last;
27147                     }
27148                 }else{
27149                     this.selectFirstRow();
27150                 }
27151                 this.fireEvent("afterselectionchange", this);
27152             },
27153             "down" : function(e){
27154                 if(!e.shiftKey){
27155                     this.selectNext(e.shiftKey);
27156                 }else if(this.last !== false && this.lastActive !== false){
27157                     var last = this.last;
27158                     this.selectRange(this.last,  this.lastActive+1);
27159                     this.grid.getView().focusRow(this.lastActive);
27160                     if(last !== false){
27161                         this.last = last;
27162                     }
27163                 }else{
27164                     this.selectFirstRow();
27165                 }
27166                 this.fireEvent("afterselectionchange", this);
27167             },
27168             scope: this
27169         });
27170         this.grid.store.on('load', function(){
27171             this.selections.clear();
27172         },this);
27173         /*
27174         var view = this.grid.view;
27175         view.on("refresh", this.onRefresh, this);
27176         view.on("rowupdated", this.onRowUpdated, this);
27177         view.on("rowremoved", this.onRemove, this);
27178         */
27179     },
27180
27181     // private
27182     onRefresh : function()
27183     {
27184         var ds = this.grid.store, i, v = this.grid.view;
27185         var s = this.selections;
27186         s.each(function(r){
27187             if((i = ds.indexOfId(r.id)) != -1){
27188                 v.onRowSelect(i);
27189             }else{
27190                 s.remove(r);
27191             }
27192         });
27193     },
27194
27195     // private
27196     onRemove : function(v, index, r){
27197         this.selections.remove(r);
27198     },
27199
27200     // private
27201     onRowUpdated : function(v, index, r){
27202         if(this.isSelected(r)){
27203             v.onRowSelect(index);
27204         }
27205     },
27206
27207     /**
27208      * Select records.
27209      * @param {Array} records The records to select
27210      * @param {Boolean} keepExisting (optional) True to keep existing selections
27211      */
27212     selectRecords : function(records, keepExisting)
27213     {
27214         if(!keepExisting){
27215             this.clearSelections();
27216         }
27217             var ds = this.grid.store;
27218         for(var i = 0, len = records.length; i < len; i++){
27219             this.selectRow(ds.indexOf(records[i]), true);
27220         }
27221     },
27222
27223     /**
27224      * Gets the number of selected rows.
27225      * @return {Number}
27226      */
27227     getCount : function(){
27228         return this.selections.length;
27229     },
27230
27231     /**
27232      * Selects the first row in the grid.
27233      */
27234     selectFirstRow : function(){
27235         this.selectRow(0);
27236     },
27237
27238     /**
27239      * Select the last row.
27240      * @param {Boolean} keepExisting (optional) True to keep existing selections
27241      */
27242     selectLastRow : function(keepExisting){
27243         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27244         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27245     },
27246
27247     /**
27248      * Selects the row immediately following the last selected row.
27249      * @param {Boolean} keepExisting (optional) True to keep existing selections
27250      */
27251     selectNext : function(keepExisting)
27252     {
27253             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27254             this.selectRow(this.last+1, keepExisting);
27255             this.grid.getView().focusRow(this.last);
27256         }
27257     },
27258
27259     /**
27260      * Selects the row that precedes the last selected row.
27261      * @param {Boolean} keepExisting (optional) True to keep existing selections
27262      */
27263     selectPrevious : function(keepExisting){
27264         if(this.last){
27265             this.selectRow(this.last-1, keepExisting);
27266             this.grid.getView().focusRow(this.last);
27267         }
27268     },
27269
27270     /**
27271      * Returns the selected records
27272      * @return {Array} Array of selected records
27273      */
27274     getSelections : function(){
27275         return [].concat(this.selections.items);
27276     },
27277
27278     /**
27279      * Returns the first selected record.
27280      * @return {Record}
27281      */
27282     getSelected : function(){
27283         return this.selections.itemAt(0);
27284     },
27285
27286
27287     /**
27288      * Clears all selections.
27289      */
27290     clearSelections : function(fast)
27291     {
27292         if(this.locked) {
27293             return;
27294         }
27295         if(fast !== true){
27296                 var ds = this.grid.store;
27297             var s = this.selections;
27298             s.each(function(r){
27299                 this.deselectRow(ds.indexOfId(r.id));
27300             }, this);
27301             s.clear();
27302         }else{
27303             this.selections.clear();
27304         }
27305         this.last = false;
27306     },
27307
27308
27309     /**
27310      * Selects all rows.
27311      */
27312     selectAll : function(){
27313         if(this.locked) {
27314             return;
27315         }
27316         this.selections.clear();
27317         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27318             this.selectRow(i, true);
27319         }
27320     },
27321
27322     /**
27323      * Returns True if there is a selection.
27324      * @return {Boolean}
27325      */
27326     hasSelection : function(){
27327         return this.selections.length > 0;
27328     },
27329
27330     /**
27331      * Returns True if the specified row is selected.
27332      * @param {Number/Record} record The record or index of the record to check
27333      * @return {Boolean}
27334      */
27335     isSelected : function(index){
27336             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27337         return (r && this.selections.key(r.id) ? true : false);
27338     },
27339
27340     /**
27341      * Returns True if the specified record id is selected.
27342      * @param {String} id The id of record to check
27343      * @return {Boolean}
27344      */
27345     isIdSelected : function(id){
27346         return (this.selections.key(id) ? true : false);
27347     },
27348
27349
27350     // private
27351     handleMouseDBClick : function(e, t){
27352         
27353     },
27354     // private
27355     handleMouseDown : function(e, t)
27356     {
27357             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27358         if(this.isLocked() || rowIndex < 0 ){
27359             return;
27360         };
27361         if(e.shiftKey && this.last !== false){
27362             var last = this.last;
27363             this.selectRange(last, rowIndex, e.ctrlKey);
27364             this.last = last; // reset the last
27365             t.focus();
27366     
27367         }else{
27368             var isSelected = this.isSelected(rowIndex);
27369             //Roo.log("select row:" + rowIndex);
27370             if(isSelected){
27371                 this.deselectRow(rowIndex);
27372             } else {
27373                         this.selectRow(rowIndex, true);
27374             }
27375     
27376             /*
27377                 if(e.button !== 0 && isSelected){
27378                 alert('rowIndex 2: ' + rowIndex);
27379                     view.focusRow(rowIndex);
27380                 }else if(e.ctrlKey && isSelected){
27381                     this.deselectRow(rowIndex);
27382                 }else if(!isSelected){
27383                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27384                     view.focusRow(rowIndex);
27385                 }
27386             */
27387         }
27388         this.fireEvent("afterselectionchange", this);
27389     },
27390     // private
27391     handleDragableRowClick :  function(grid, rowIndex, e) 
27392     {
27393         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27394             this.selectRow(rowIndex, false);
27395             grid.view.focusRow(rowIndex);
27396              this.fireEvent("afterselectionchange", this);
27397         }
27398     },
27399     
27400     /**
27401      * Selects multiple rows.
27402      * @param {Array} rows Array of the indexes of the row to select
27403      * @param {Boolean} keepExisting (optional) True to keep existing selections
27404      */
27405     selectRows : function(rows, keepExisting){
27406         if(!keepExisting){
27407             this.clearSelections();
27408         }
27409         for(var i = 0, len = rows.length; i < len; i++){
27410             this.selectRow(rows[i], true);
27411         }
27412     },
27413
27414     /**
27415      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27416      * @param {Number} startRow The index of the first row in the range
27417      * @param {Number} endRow The index of the last row in the range
27418      * @param {Boolean} keepExisting (optional) True to retain existing selections
27419      */
27420     selectRange : function(startRow, endRow, keepExisting){
27421         if(this.locked) {
27422             return;
27423         }
27424         if(!keepExisting){
27425             this.clearSelections();
27426         }
27427         if(startRow <= endRow){
27428             for(var i = startRow; i <= endRow; i++){
27429                 this.selectRow(i, true);
27430             }
27431         }else{
27432             for(var i = startRow; i >= endRow; i--){
27433                 this.selectRow(i, true);
27434             }
27435         }
27436     },
27437
27438     /**
27439      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27440      * @param {Number} startRow The index of the first row in the range
27441      * @param {Number} endRow The index of the last row in the range
27442      */
27443     deselectRange : function(startRow, endRow, preventViewNotify){
27444         if(this.locked) {
27445             return;
27446         }
27447         for(var i = startRow; i <= endRow; i++){
27448             this.deselectRow(i, preventViewNotify);
27449         }
27450     },
27451
27452     /**
27453      * Selects a row.
27454      * @param {Number} row The index of the row to select
27455      * @param {Boolean} keepExisting (optional) True to keep existing selections
27456      */
27457     selectRow : function(index, keepExisting, preventViewNotify)
27458     {
27459             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27460             return;
27461         }
27462         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27463             if(!keepExisting || this.singleSelect){
27464                 this.clearSelections();
27465             }
27466             
27467             var r = this.grid.store.getAt(index);
27468             //console.log('selectRow - record id :' + r.id);
27469             
27470             this.selections.add(r);
27471             this.last = this.lastActive = index;
27472             if(!preventViewNotify){
27473                 var proxy = new Roo.Element(
27474                                 this.grid.getRowDom(index)
27475                 );
27476                 proxy.addClass('bg-info info');
27477             }
27478             this.fireEvent("rowselect", this, index, r);
27479             this.fireEvent("selectionchange", this);
27480         }
27481     },
27482
27483     /**
27484      * Deselects a row.
27485      * @param {Number} row The index of the row to deselect
27486      */
27487     deselectRow : function(index, preventViewNotify)
27488     {
27489         if(this.locked) {
27490             return;
27491         }
27492         if(this.last == index){
27493             this.last = false;
27494         }
27495         if(this.lastActive == index){
27496             this.lastActive = false;
27497         }
27498         
27499         var r = this.grid.store.getAt(index);
27500         if (!r) {
27501             return;
27502         }
27503         
27504         this.selections.remove(r);
27505         //.console.log('deselectRow - record id :' + r.id);
27506         if(!preventViewNotify){
27507         
27508             var proxy = new Roo.Element(
27509                 this.grid.getRowDom(index)
27510             );
27511             proxy.removeClass('bg-info info');
27512         }
27513         this.fireEvent("rowdeselect", this, index);
27514         this.fireEvent("selectionchange", this);
27515     },
27516
27517     // private
27518     restoreLast : function(){
27519         if(this._last){
27520             this.last = this._last;
27521         }
27522     },
27523
27524     // private
27525     acceptsNav : function(row, col, cm){
27526         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27527     },
27528
27529     // private
27530     onEditorKey : function(field, e){
27531         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27532         if(k == e.TAB){
27533             e.stopEvent();
27534             ed.completeEdit();
27535             if(e.shiftKey){
27536                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27537             }else{
27538                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27539             }
27540         }else if(k == e.ENTER && !e.ctrlKey){
27541             e.stopEvent();
27542             ed.completeEdit();
27543             if(e.shiftKey){
27544                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27545             }else{
27546                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27547             }
27548         }else if(k == e.ESC){
27549             ed.cancelEdit();
27550         }
27551         if(newCell){
27552             g.startEditing(newCell[0], newCell[1]);
27553         }
27554     }
27555 });
27556 /*
27557  * Based on:
27558  * Ext JS Library 1.1.1
27559  * Copyright(c) 2006-2007, Ext JS, LLC.
27560  *
27561  * Originally Released Under LGPL - original licence link has changed is not relivant.
27562  *
27563  * Fork - LGPL
27564  * <script type="text/javascript">
27565  */
27566  
27567 /**
27568  * @class Roo.bootstrap.PagingToolbar
27569  * @extends Roo.bootstrap.NavSimplebar
27570  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27571  * @constructor
27572  * Create a new PagingToolbar
27573  * @param {Object} config The config object
27574  * @param {Roo.data.Store} store
27575  */
27576 Roo.bootstrap.PagingToolbar = function(config)
27577 {
27578     // old args format still supported... - xtype is prefered..
27579         // created from xtype...
27580     
27581     this.ds = config.dataSource;
27582     
27583     if (config.store && !this.ds) {
27584         this.store= Roo.factory(config.store, Roo.data);
27585         this.ds = this.store;
27586         this.ds.xmodule = this.xmodule || false;
27587     }
27588     
27589     this.toolbarItems = [];
27590     if (config.items) {
27591         this.toolbarItems = config.items;
27592     }
27593     
27594     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27595     
27596     this.cursor = 0;
27597     
27598     if (this.ds) { 
27599         this.bind(this.ds);
27600     }
27601     
27602     if (Roo.bootstrap.version == 4) {
27603         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27604     } else {
27605         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27606     }
27607     
27608 };
27609
27610 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27611     /**
27612      * @cfg {Roo.data.Store} dataSource
27613      * The underlying data store providing the paged data
27614      */
27615     /**
27616      * @cfg {String/HTMLElement/Element} container
27617      * container The id or element that will contain the toolbar
27618      */
27619     /**
27620      * @cfg {Boolean} displayInfo
27621      * True to display the displayMsg (defaults to false)
27622      */
27623     /**
27624      * @cfg {Number} pageSize
27625      * The number of records to display per page (defaults to 20)
27626      */
27627     pageSize: 20,
27628     /**
27629      * @cfg {String} displayMsg
27630      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27631      */
27632     displayMsg : 'Displaying {0} - {1} of {2}',
27633     /**
27634      * @cfg {String} emptyMsg
27635      * The message to display when no records are found (defaults to "No data to display")
27636      */
27637     emptyMsg : 'No data to display',
27638     /**
27639      * Customizable piece of the default paging text (defaults to "Page")
27640      * @type String
27641      */
27642     beforePageText : "Page",
27643     /**
27644      * Customizable piece of the default paging text (defaults to "of %0")
27645      * @type String
27646      */
27647     afterPageText : "of {0}",
27648     /**
27649      * Customizable piece of the default paging text (defaults to "First Page")
27650      * @type String
27651      */
27652     firstText : "First Page",
27653     /**
27654      * Customizable piece of the default paging text (defaults to "Previous Page")
27655      * @type String
27656      */
27657     prevText : "Previous Page",
27658     /**
27659      * Customizable piece of the default paging text (defaults to "Next Page")
27660      * @type String
27661      */
27662     nextText : "Next Page",
27663     /**
27664      * Customizable piece of the default paging text (defaults to "Last Page")
27665      * @type String
27666      */
27667     lastText : "Last Page",
27668     /**
27669      * Customizable piece of the default paging text (defaults to "Refresh")
27670      * @type String
27671      */
27672     refreshText : "Refresh",
27673
27674     buttons : false,
27675     // private
27676     onRender : function(ct, position) 
27677     {
27678         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27679         this.navgroup.parentId = this.id;
27680         this.navgroup.onRender(this.el, null);
27681         // add the buttons to the navgroup
27682         
27683         if(this.displayInfo){
27684             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27685             this.displayEl = this.el.select('.x-paging-info', true).first();
27686 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27687 //            this.displayEl = navel.el.select('span',true).first();
27688         }
27689         
27690         var _this = this;
27691         
27692         if(this.buttons){
27693             Roo.each(_this.buttons, function(e){ // this might need to use render????
27694                Roo.factory(e).render(_this.el);
27695             });
27696         }
27697             
27698         Roo.each(_this.toolbarItems, function(e) {
27699             _this.navgroup.addItem(e);
27700         });
27701         
27702         
27703         this.first = this.navgroup.addItem({
27704             tooltip: this.firstText,
27705             cls: "prev btn-outline-secondary",
27706             html : ' <i class="fa fa-step-backward"></i>',
27707             disabled: true,
27708             preventDefault: true,
27709             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27710         });
27711         
27712         this.prev =  this.navgroup.addItem({
27713             tooltip: this.prevText,
27714             cls: "prev btn-outline-secondary",
27715             html : ' <i class="fa fa-backward"></i>',
27716             disabled: true,
27717             preventDefault: true,
27718             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27719         });
27720     //this.addSeparator();
27721         
27722         
27723         var field = this.navgroup.addItem( {
27724             tagtype : 'span',
27725             cls : 'x-paging-position  btn-outline-secondary',
27726              disabled: true,
27727             html : this.beforePageText  +
27728                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27729                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27730          } ); //?? escaped?
27731         
27732         this.field = field.el.select('input', true).first();
27733         this.field.on("keydown", this.onPagingKeydown, this);
27734         this.field.on("focus", function(){this.dom.select();});
27735     
27736     
27737         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27738         //this.field.setHeight(18);
27739         //this.addSeparator();
27740         this.next = this.navgroup.addItem({
27741             tooltip: this.nextText,
27742             cls: "next btn-outline-secondary",
27743             html : ' <i class="fa fa-forward"></i>',
27744             disabled: true,
27745             preventDefault: true,
27746             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27747         });
27748         this.last = this.navgroup.addItem({
27749             tooltip: this.lastText,
27750             html : ' <i class="fa fa-step-forward"></i>',
27751             cls: "next btn-outline-secondary",
27752             disabled: true,
27753             preventDefault: true,
27754             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27755         });
27756     //this.addSeparator();
27757         this.loading = this.navgroup.addItem({
27758             tooltip: this.refreshText,
27759             cls: "btn-outline-secondary",
27760             html : ' <i class="fa fa-refresh"></i>',
27761             preventDefault: true,
27762             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27763         });
27764         
27765     },
27766
27767     // private
27768     updateInfo : function(){
27769         if(this.displayEl){
27770             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27771             var msg = count == 0 ?
27772                 this.emptyMsg :
27773                 String.format(
27774                     this.displayMsg,
27775                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27776                 );
27777             this.displayEl.update(msg);
27778         }
27779     },
27780
27781     // private
27782     onLoad : function(ds, r, o)
27783     {
27784         this.cursor = o.params && o.params.start ? o.params.start : 0;
27785         
27786         var d = this.getPageData(),
27787             ap = d.activePage,
27788             ps = d.pages;
27789         
27790         
27791         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27792         this.field.dom.value = ap;
27793         this.first.setDisabled(ap == 1);
27794         this.prev.setDisabled(ap == 1);
27795         this.next.setDisabled(ap == ps);
27796         this.last.setDisabled(ap == ps);
27797         this.loading.enable();
27798         this.updateInfo();
27799     },
27800
27801     // private
27802     getPageData : function(){
27803         var total = this.ds.getTotalCount();
27804         return {
27805             total : total,
27806             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27807             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27808         };
27809     },
27810
27811     // private
27812     onLoadError : function(){
27813         this.loading.enable();
27814     },
27815
27816     // private
27817     onPagingKeydown : function(e){
27818         var k = e.getKey();
27819         var d = this.getPageData();
27820         if(k == e.RETURN){
27821             var v = this.field.dom.value, pageNum;
27822             if(!v || isNaN(pageNum = parseInt(v, 10))){
27823                 this.field.dom.value = d.activePage;
27824                 return;
27825             }
27826             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27827             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27828             e.stopEvent();
27829         }
27830         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))
27831         {
27832           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27833           this.field.dom.value = pageNum;
27834           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27835           e.stopEvent();
27836         }
27837         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27838         {
27839           var v = this.field.dom.value, pageNum; 
27840           var increment = (e.shiftKey) ? 10 : 1;
27841           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27842                 increment *= -1;
27843           }
27844           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27845             this.field.dom.value = d.activePage;
27846             return;
27847           }
27848           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27849           {
27850             this.field.dom.value = parseInt(v, 10) + increment;
27851             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27852             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27853           }
27854           e.stopEvent();
27855         }
27856     },
27857
27858     // private
27859     beforeLoad : function(){
27860         if(this.loading){
27861             this.loading.disable();
27862         }
27863     },
27864
27865     // private
27866     onClick : function(which){
27867         
27868         var ds = this.ds;
27869         if (!ds) {
27870             return;
27871         }
27872         
27873         switch(which){
27874             case "first":
27875                 ds.load({params:{start: 0, limit: this.pageSize}});
27876             break;
27877             case "prev":
27878                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27879             break;
27880             case "next":
27881                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27882             break;
27883             case "last":
27884                 var total = ds.getTotalCount();
27885                 var extra = total % this.pageSize;
27886                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27887                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27888             break;
27889             case "refresh":
27890                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27891             break;
27892         }
27893     },
27894
27895     /**
27896      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27897      * @param {Roo.data.Store} store The data store to unbind
27898      */
27899     unbind : function(ds){
27900         ds.un("beforeload", this.beforeLoad, this);
27901         ds.un("load", this.onLoad, this);
27902         ds.un("loadexception", this.onLoadError, this);
27903         ds.un("remove", this.updateInfo, this);
27904         ds.un("add", this.updateInfo, this);
27905         this.ds = undefined;
27906     },
27907
27908     /**
27909      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27910      * @param {Roo.data.Store} store The data store to bind
27911      */
27912     bind : function(ds){
27913         ds.on("beforeload", this.beforeLoad, this);
27914         ds.on("load", this.onLoad, this);
27915         ds.on("loadexception", this.onLoadError, this);
27916         ds.on("remove", this.updateInfo, this);
27917         ds.on("add", this.updateInfo, this);
27918         this.ds = ds;
27919     }
27920 });/*
27921  * - LGPL
27922  *
27923  * element
27924  * 
27925  */
27926
27927 /**
27928  * @class Roo.bootstrap.MessageBar
27929  * @extends Roo.bootstrap.Component
27930  * Bootstrap MessageBar class
27931  * @cfg {String} html contents of the MessageBar
27932  * @cfg {String} weight (info | success | warning | danger) default info
27933  * @cfg {String} beforeClass insert the bar before the given class
27934  * @cfg {Boolean} closable (true | false) default false
27935  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27936  * 
27937  * @constructor
27938  * Create a new Element
27939  * @param {Object} config The config object
27940  */
27941
27942 Roo.bootstrap.MessageBar = function(config){
27943     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27944 };
27945
27946 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27947     
27948     html: '',
27949     weight: 'info',
27950     closable: false,
27951     fixed: false,
27952     beforeClass: 'bootstrap-sticky-wrap',
27953     
27954     getAutoCreate : function(){
27955         
27956         var cfg = {
27957             tag: 'div',
27958             cls: 'alert alert-dismissable alert-' + this.weight,
27959             cn: [
27960                 {
27961                     tag: 'span',
27962                     cls: 'message',
27963                     html: this.html || ''
27964                 }
27965             ]
27966         };
27967         
27968         if(this.fixed){
27969             cfg.cls += ' alert-messages-fixed';
27970         }
27971         
27972         if(this.closable){
27973             cfg.cn.push({
27974                 tag: 'button',
27975                 cls: 'close',
27976                 html: 'x'
27977             });
27978         }
27979         
27980         return cfg;
27981     },
27982     
27983     onRender : function(ct, position)
27984     {
27985         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27986         
27987         if(!this.el){
27988             var cfg = Roo.apply({},  this.getAutoCreate());
27989             cfg.id = Roo.id();
27990             
27991             if (this.cls) {
27992                 cfg.cls += ' ' + this.cls;
27993             }
27994             if (this.style) {
27995                 cfg.style = this.style;
27996             }
27997             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27998             
27999             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28000         }
28001         
28002         this.el.select('>button.close').on('click', this.hide, this);
28003         
28004     },
28005     
28006     show : function()
28007     {
28008         if (!this.rendered) {
28009             this.render();
28010         }
28011         
28012         this.el.show();
28013         
28014         this.fireEvent('show', this);
28015         
28016     },
28017     
28018     hide : function()
28019     {
28020         if (!this.rendered) {
28021             this.render();
28022         }
28023         
28024         this.el.hide();
28025         
28026         this.fireEvent('hide', this);
28027     },
28028     
28029     update : function()
28030     {
28031 //        var e = this.el.dom.firstChild;
28032 //        
28033 //        if(this.closable){
28034 //            e = e.nextSibling;
28035 //        }
28036 //        
28037 //        e.data = this.html || '';
28038
28039         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28040     }
28041    
28042 });
28043
28044  
28045
28046      /*
28047  * - LGPL
28048  *
28049  * Graph
28050  * 
28051  */
28052
28053
28054 /**
28055  * @class Roo.bootstrap.Graph
28056  * @extends Roo.bootstrap.Component
28057  * Bootstrap Graph class
28058 > Prameters
28059  -sm {number} sm 4
28060  -md {number} md 5
28061  @cfg {String} graphtype  bar | vbar | pie
28062  @cfg {number} g_x coodinator | centre x (pie)
28063  @cfg {number} g_y coodinator | centre y (pie)
28064  @cfg {number} g_r radius (pie)
28065  @cfg {number} g_height height of the chart (respected by all elements in the set)
28066  @cfg {number} g_width width of the chart (respected by all elements in the set)
28067  @cfg {Object} title The title of the chart
28068     
28069  -{Array}  values
28070  -opts (object) options for the chart 
28071      o {
28072      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28073      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28074      o vgutter (number)
28075      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.
28076      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28077      o to
28078      o stretch (boolean)
28079      o }
28080  -opts (object) options for the pie
28081      o{
28082      o cut
28083      o startAngle (number)
28084      o endAngle (number)
28085      } 
28086  *
28087  * @constructor
28088  * Create a new Input
28089  * @param {Object} config The config object
28090  */
28091
28092 Roo.bootstrap.Graph = function(config){
28093     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28094     
28095     this.addEvents({
28096         // img events
28097         /**
28098          * @event click
28099          * The img click event for the img.
28100          * @param {Roo.EventObject} e
28101          */
28102         "click" : true
28103     });
28104 };
28105
28106 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28107     
28108     sm: 4,
28109     md: 5,
28110     graphtype: 'bar',
28111     g_height: 250,
28112     g_width: 400,
28113     g_x: 50,
28114     g_y: 50,
28115     g_r: 30,
28116     opts:{
28117         //g_colors: this.colors,
28118         g_type: 'soft',
28119         g_gutter: '20%'
28120
28121     },
28122     title : false,
28123
28124     getAutoCreate : function(){
28125         
28126         var cfg = {
28127             tag: 'div',
28128             html : null
28129         };
28130         
28131         
28132         return  cfg;
28133     },
28134
28135     onRender : function(ct,position){
28136         
28137         
28138         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28139         
28140         if (typeof(Raphael) == 'undefined') {
28141             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28142             return;
28143         }
28144         
28145         this.raphael = Raphael(this.el.dom);
28146         
28147                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28148                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28149                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28150                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28151                 /*
28152                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28153                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28154                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28155                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28156                 
28157                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28158                 r.barchart(330, 10, 300, 220, data1);
28159                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28160                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28161                 */
28162                 
28163                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28164                 // r.barchart(30, 30, 560, 250,  xdata, {
28165                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28166                 //     axis : "0 0 1 1",
28167                 //     axisxlabels :  xdata
28168                 //     //yvalues : cols,
28169                    
28170                 // });
28171 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28172 //        
28173 //        this.load(null,xdata,{
28174 //                axis : "0 0 1 1",
28175 //                axisxlabels :  xdata
28176 //                });
28177
28178     },
28179
28180     load : function(graphtype,xdata,opts)
28181     {
28182         this.raphael.clear();
28183         if(!graphtype) {
28184             graphtype = this.graphtype;
28185         }
28186         if(!opts){
28187             opts = this.opts;
28188         }
28189         var r = this.raphael,
28190             fin = function () {
28191                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28192             },
28193             fout = function () {
28194                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28195             },
28196             pfin = function() {
28197                 this.sector.stop();
28198                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28199
28200                 if (this.label) {
28201                     this.label[0].stop();
28202                     this.label[0].attr({ r: 7.5 });
28203                     this.label[1].attr({ "font-weight": 800 });
28204                 }
28205             },
28206             pfout = function() {
28207                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28208
28209                 if (this.label) {
28210                     this.label[0].animate({ r: 5 }, 500, "bounce");
28211                     this.label[1].attr({ "font-weight": 400 });
28212                 }
28213             };
28214
28215         switch(graphtype){
28216             case 'bar':
28217                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28218                 break;
28219             case 'hbar':
28220                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28221                 break;
28222             case 'pie':
28223 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28224 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28225 //            
28226                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28227                 
28228                 break;
28229
28230         }
28231         
28232         if(this.title){
28233             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28234         }
28235         
28236     },
28237     
28238     setTitle: function(o)
28239     {
28240         this.title = o;
28241     },
28242     
28243     initEvents: function() {
28244         
28245         if(!this.href){
28246             this.el.on('click', this.onClick, this);
28247         }
28248     },
28249     
28250     onClick : function(e)
28251     {
28252         Roo.log('img onclick');
28253         this.fireEvent('click', this, e);
28254     }
28255    
28256 });
28257
28258  
28259 /*
28260  * - LGPL
28261  *
28262  * numberBox
28263  * 
28264  */
28265 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28266
28267 /**
28268  * @class Roo.bootstrap.dash.NumberBox
28269  * @extends Roo.bootstrap.Component
28270  * Bootstrap NumberBox class
28271  * @cfg {String} headline Box headline
28272  * @cfg {String} content Box content
28273  * @cfg {String} icon Box icon
28274  * @cfg {String} footer Footer text
28275  * @cfg {String} fhref Footer href
28276  * 
28277  * @constructor
28278  * Create a new NumberBox
28279  * @param {Object} config The config object
28280  */
28281
28282
28283 Roo.bootstrap.dash.NumberBox = function(config){
28284     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28285     
28286 };
28287
28288 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28289     
28290     headline : '',
28291     content : '',
28292     icon : '',
28293     footer : '',
28294     fhref : '',
28295     ficon : '',
28296     
28297     getAutoCreate : function(){
28298         
28299         var cfg = {
28300             tag : 'div',
28301             cls : 'small-box ',
28302             cn : [
28303                 {
28304                     tag : 'div',
28305                     cls : 'inner',
28306                     cn :[
28307                         {
28308                             tag : 'h3',
28309                             cls : 'roo-headline',
28310                             html : this.headline
28311                         },
28312                         {
28313                             tag : 'p',
28314                             cls : 'roo-content',
28315                             html : this.content
28316                         }
28317                     ]
28318                 }
28319             ]
28320         };
28321         
28322         if(this.icon){
28323             cfg.cn.push({
28324                 tag : 'div',
28325                 cls : 'icon',
28326                 cn :[
28327                     {
28328                         tag : 'i',
28329                         cls : 'ion ' + this.icon
28330                     }
28331                 ]
28332             });
28333         }
28334         
28335         if(this.footer){
28336             var footer = {
28337                 tag : 'a',
28338                 cls : 'small-box-footer',
28339                 href : this.fhref || '#',
28340                 html : this.footer
28341             };
28342             
28343             cfg.cn.push(footer);
28344             
28345         }
28346         
28347         return  cfg;
28348     },
28349
28350     onRender : function(ct,position){
28351         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28352
28353
28354        
28355                 
28356     },
28357
28358     setHeadline: function (value)
28359     {
28360         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28361     },
28362     
28363     setFooter: function (value, href)
28364     {
28365         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28366         
28367         if(href){
28368             this.el.select('a.small-box-footer',true).first().attr('href', href);
28369         }
28370         
28371     },
28372
28373     setContent: function (value)
28374     {
28375         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28376     },
28377
28378     initEvents: function() 
28379     {   
28380         
28381     }
28382     
28383 });
28384
28385  
28386 /*
28387  * - LGPL
28388  *
28389  * TabBox
28390  * 
28391  */
28392 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28393
28394 /**
28395  * @class Roo.bootstrap.dash.TabBox
28396  * @extends Roo.bootstrap.Component
28397  * Bootstrap TabBox class
28398  * @cfg {String} title Title of the TabBox
28399  * @cfg {String} icon Icon of the TabBox
28400  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28401  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28402  * 
28403  * @constructor
28404  * Create a new TabBox
28405  * @param {Object} config The config object
28406  */
28407
28408
28409 Roo.bootstrap.dash.TabBox = function(config){
28410     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28411     this.addEvents({
28412         // raw events
28413         /**
28414          * @event addpane
28415          * When a pane is added
28416          * @param {Roo.bootstrap.dash.TabPane} pane
28417          */
28418         "addpane" : true,
28419         /**
28420          * @event activatepane
28421          * When a pane is activated
28422          * @param {Roo.bootstrap.dash.TabPane} pane
28423          */
28424         "activatepane" : true
28425         
28426          
28427     });
28428     
28429     this.panes = [];
28430 };
28431
28432 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28433
28434     title : '',
28435     icon : false,
28436     showtabs : true,
28437     tabScrollable : false,
28438     
28439     getChildContainer : function()
28440     {
28441         return this.el.select('.tab-content', true).first();
28442     },
28443     
28444     getAutoCreate : function(){
28445         
28446         var header = {
28447             tag: 'li',
28448             cls: 'pull-left header',
28449             html: this.title,
28450             cn : []
28451         };
28452         
28453         if(this.icon){
28454             header.cn.push({
28455                 tag: 'i',
28456                 cls: 'fa ' + this.icon
28457             });
28458         }
28459         
28460         var h = {
28461             tag: 'ul',
28462             cls: 'nav nav-tabs pull-right',
28463             cn: [
28464                 header
28465             ]
28466         };
28467         
28468         if(this.tabScrollable){
28469             h = {
28470                 tag: 'div',
28471                 cls: 'tab-header',
28472                 cn: [
28473                     {
28474                         tag: 'ul',
28475                         cls: 'nav nav-tabs pull-right',
28476                         cn: [
28477                             header
28478                         ]
28479                     }
28480                 ]
28481             };
28482         }
28483         
28484         var cfg = {
28485             tag: 'div',
28486             cls: 'nav-tabs-custom',
28487             cn: [
28488                 h,
28489                 {
28490                     tag: 'div',
28491                     cls: 'tab-content no-padding',
28492                     cn: []
28493                 }
28494             ]
28495         };
28496
28497         return  cfg;
28498     },
28499     initEvents : function()
28500     {
28501         //Roo.log('add add pane handler');
28502         this.on('addpane', this.onAddPane, this);
28503     },
28504      /**
28505      * Updates the box title
28506      * @param {String} html to set the title to.
28507      */
28508     setTitle : function(value)
28509     {
28510         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28511     },
28512     onAddPane : function(pane)
28513     {
28514         this.panes.push(pane);
28515         //Roo.log('addpane');
28516         //Roo.log(pane);
28517         // tabs are rendere left to right..
28518         if(!this.showtabs){
28519             return;
28520         }
28521         
28522         var ctr = this.el.select('.nav-tabs', true).first();
28523          
28524          
28525         var existing = ctr.select('.nav-tab',true);
28526         var qty = existing.getCount();;
28527         
28528         
28529         var tab = ctr.createChild({
28530             tag : 'li',
28531             cls : 'nav-tab' + (qty ? '' : ' active'),
28532             cn : [
28533                 {
28534                     tag : 'a',
28535                     href:'#',
28536                     html : pane.title
28537                 }
28538             ]
28539         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28540         pane.tab = tab;
28541         
28542         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28543         if (!qty) {
28544             pane.el.addClass('active');
28545         }
28546         
28547                 
28548     },
28549     onTabClick : function(ev,un,ob,pane)
28550     {
28551         //Roo.log('tab - prev default');
28552         ev.preventDefault();
28553         
28554         
28555         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28556         pane.tab.addClass('active');
28557         //Roo.log(pane.title);
28558         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28559         // technically we should have a deactivate event.. but maybe add later.
28560         // and it should not de-activate the selected tab...
28561         this.fireEvent('activatepane', pane);
28562         pane.el.addClass('active');
28563         pane.fireEvent('activate');
28564         
28565         
28566     },
28567     
28568     getActivePane : function()
28569     {
28570         var r = false;
28571         Roo.each(this.panes, function(p) {
28572             if(p.el.hasClass('active')){
28573                 r = p;
28574                 return false;
28575             }
28576             
28577             return;
28578         });
28579         
28580         return r;
28581     }
28582     
28583     
28584 });
28585
28586  
28587 /*
28588  * - LGPL
28589  *
28590  * Tab pane
28591  * 
28592  */
28593 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28594 /**
28595  * @class Roo.bootstrap.TabPane
28596  * @extends Roo.bootstrap.Component
28597  * Bootstrap TabPane class
28598  * @cfg {Boolean} active (false | true) Default false
28599  * @cfg {String} title title of panel
28600
28601  * 
28602  * @constructor
28603  * Create a new TabPane
28604  * @param {Object} config The config object
28605  */
28606
28607 Roo.bootstrap.dash.TabPane = function(config){
28608     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28609     
28610     this.addEvents({
28611         // raw events
28612         /**
28613          * @event activate
28614          * When a pane is activated
28615          * @param {Roo.bootstrap.dash.TabPane} pane
28616          */
28617         "activate" : true
28618          
28619     });
28620 };
28621
28622 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28623     
28624     active : false,
28625     title : '',
28626     
28627     // the tabBox that this is attached to.
28628     tab : false,
28629      
28630     getAutoCreate : function() 
28631     {
28632         var cfg = {
28633             tag: 'div',
28634             cls: 'tab-pane'
28635         };
28636         
28637         if(this.active){
28638             cfg.cls += ' active';
28639         }
28640         
28641         return cfg;
28642     },
28643     initEvents  : function()
28644     {
28645         //Roo.log('trigger add pane handler');
28646         this.parent().fireEvent('addpane', this)
28647     },
28648     
28649      /**
28650      * Updates the tab title 
28651      * @param {String} html to set the title to.
28652      */
28653     setTitle: function(str)
28654     {
28655         if (!this.tab) {
28656             return;
28657         }
28658         this.title = str;
28659         this.tab.select('a', true).first().dom.innerHTML = str;
28660         
28661     }
28662     
28663     
28664     
28665 });
28666
28667  
28668
28669
28670  /*
28671  * - LGPL
28672  *
28673  * menu
28674  * 
28675  */
28676 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28677
28678 /**
28679  * @class Roo.bootstrap.menu.Menu
28680  * @extends Roo.bootstrap.Component
28681  * Bootstrap Menu class - container for Menu
28682  * @cfg {String} html Text of the menu
28683  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28684  * @cfg {String} icon Font awesome icon
28685  * @cfg {String} pos Menu align to (top | bottom) default bottom
28686  * 
28687  * 
28688  * @constructor
28689  * Create a new Menu
28690  * @param {Object} config The config object
28691  */
28692
28693
28694 Roo.bootstrap.menu.Menu = function(config){
28695     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28696     
28697     this.addEvents({
28698         /**
28699          * @event beforeshow
28700          * Fires before this menu is displayed
28701          * @param {Roo.bootstrap.menu.Menu} this
28702          */
28703         beforeshow : true,
28704         /**
28705          * @event beforehide
28706          * Fires before this menu is hidden
28707          * @param {Roo.bootstrap.menu.Menu} this
28708          */
28709         beforehide : true,
28710         /**
28711          * @event show
28712          * Fires after this menu is displayed
28713          * @param {Roo.bootstrap.menu.Menu} this
28714          */
28715         show : true,
28716         /**
28717          * @event hide
28718          * Fires after this menu is hidden
28719          * @param {Roo.bootstrap.menu.Menu} this
28720          */
28721         hide : true,
28722         /**
28723          * @event click
28724          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28725          * @param {Roo.bootstrap.menu.Menu} this
28726          * @param {Roo.EventObject} e
28727          */
28728         click : true
28729     });
28730     
28731 };
28732
28733 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28734     
28735     submenu : false,
28736     html : '',
28737     weight : 'default',
28738     icon : false,
28739     pos : 'bottom',
28740     
28741     
28742     getChildContainer : function() {
28743         if(this.isSubMenu){
28744             return this.el;
28745         }
28746         
28747         return this.el.select('ul.dropdown-menu', true).first();  
28748     },
28749     
28750     getAutoCreate : function()
28751     {
28752         var text = [
28753             {
28754                 tag : 'span',
28755                 cls : 'roo-menu-text',
28756                 html : this.html
28757             }
28758         ];
28759         
28760         if(this.icon){
28761             text.unshift({
28762                 tag : 'i',
28763                 cls : 'fa ' + this.icon
28764             })
28765         }
28766         
28767         
28768         var cfg = {
28769             tag : 'div',
28770             cls : 'btn-group',
28771             cn : [
28772                 {
28773                     tag : 'button',
28774                     cls : 'dropdown-button btn btn-' + this.weight,
28775                     cn : text
28776                 },
28777                 {
28778                     tag : 'button',
28779                     cls : 'dropdown-toggle btn btn-' + this.weight,
28780                     cn : [
28781                         {
28782                             tag : 'span',
28783                             cls : 'caret'
28784                         }
28785                     ]
28786                 },
28787                 {
28788                     tag : 'ul',
28789                     cls : 'dropdown-menu'
28790                 }
28791             ]
28792             
28793         };
28794         
28795         if(this.pos == 'top'){
28796             cfg.cls += ' dropup';
28797         }
28798         
28799         if(this.isSubMenu){
28800             cfg = {
28801                 tag : 'ul',
28802                 cls : 'dropdown-menu'
28803             }
28804         }
28805         
28806         return cfg;
28807     },
28808     
28809     onRender : function(ct, position)
28810     {
28811         this.isSubMenu = ct.hasClass('dropdown-submenu');
28812         
28813         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28814     },
28815     
28816     initEvents : function() 
28817     {
28818         if(this.isSubMenu){
28819             return;
28820         }
28821         
28822         this.hidden = true;
28823         
28824         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28825         this.triggerEl.on('click', this.onTriggerPress, this);
28826         
28827         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28828         this.buttonEl.on('click', this.onClick, this);
28829         
28830     },
28831     
28832     list : function()
28833     {
28834         if(this.isSubMenu){
28835             return this.el;
28836         }
28837         
28838         return this.el.select('ul.dropdown-menu', true).first();
28839     },
28840     
28841     onClick : function(e)
28842     {
28843         this.fireEvent("click", this, e);
28844     },
28845     
28846     onTriggerPress  : function(e)
28847     {   
28848         if (this.isVisible()) {
28849             this.hide();
28850         } else {
28851             this.show();
28852         }
28853     },
28854     
28855     isVisible : function(){
28856         return !this.hidden;
28857     },
28858     
28859     show : function()
28860     {
28861         this.fireEvent("beforeshow", this);
28862         
28863         this.hidden = false;
28864         this.el.addClass('open');
28865         
28866         Roo.get(document).on("mouseup", this.onMouseUp, this);
28867         
28868         this.fireEvent("show", this);
28869         
28870         
28871     },
28872     
28873     hide : function()
28874     {
28875         this.fireEvent("beforehide", this);
28876         
28877         this.hidden = true;
28878         this.el.removeClass('open');
28879         
28880         Roo.get(document).un("mouseup", this.onMouseUp);
28881         
28882         this.fireEvent("hide", this);
28883     },
28884     
28885     onMouseUp : function()
28886     {
28887         this.hide();
28888     }
28889     
28890 });
28891
28892  
28893  /*
28894  * - LGPL
28895  *
28896  * menu item
28897  * 
28898  */
28899 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28900
28901 /**
28902  * @class Roo.bootstrap.menu.Item
28903  * @extends Roo.bootstrap.Component
28904  * Bootstrap MenuItem class
28905  * @cfg {Boolean} submenu (true | false) default false
28906  * @cfg {String} html text of the item
28907  * @cfg {String} href the link
28908  * @cfg {Boolean} disable (true | false) default false
28909  * @cfg {Boolean} preventDefault (true | false) default true
28910  * @cfg {String} icon Font awesome icon
28911  * @cfg {String} pos Submenu align to (left | right) default right 
28912  * 
28913  * 
28914  * @constructor
28915  * Create a new Item
28916  * @param {Object} config The config object
28917  */
28918
28919
28920 Roo.bootstrap.menu.Item = function(config){
28921     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28922     this.addEvents({
28923         /**
28924          * @event mouseover
28925          * Fires when the mouse is hovering over this menu
28926          * @param {Roo.bootstrap.menu.Item} this
28927          * @param {Roo.EventObject} e
28928          */
28929         mouseover : true,
28930         /**
28931          * @event mouseout
28932          * Fires when the mouse exits this menu
28933          * @param {Roo.bootstrap.menu.Item} this
28934          * @param {Roo.EventObject} e
28935          */
28936         mouseout : true,
28937         // raw events
28938         /**
28939          * @event click
28940          * The raw click event for the entire grid.
28941          * @param {Roo.EventObject} e
28942          */
28943         click : true
28944     });
28945 };
28946
28947 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28948     
28949     submenu : false,
28950     href : '',
28951     html : '',
28952     preventDefault: true,
28953     disable : false,
28954     icon : false,
28955     pos : 'right',
28956     
28957     getAutoCreate : function()
28958     {
28959         var text = [
28960             {
28961                 tag : 'span',
28962                 cls : 'roo-menu-item-text',
28963                 html : this.html
28964             }
28965         ];
28966         
28967         if(this.icon){
28968             text.unshift({
28969                 tag : 'i',
28970                 cls : 'fa ' + this.icon
28971             })
28972         }
28973         
28974         var cfg = {
28975             tag : 'li',
28976             cn : [
28977                 {
28978                     tag : 'a',
28979                     href : this.href || '#',
28980                     cn : text
28981                 }
28982             ]
28983         };
28984         
28985         if(this.disable){
28986             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28987         }
28988         
28989         if(this.submenu){
28990             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28991             
28992             if(this.pos == 'left'){
28993                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28994             }
28995         }
28996         
28997         return cfg;
28998     },
28999     
29000     initEvents : function() 
29001     {
29002         this.el.on('mouseover', this.onMouseOver, this);
29003         this.el.on('mouseout', this.onMouseOut, this);
29004         
29005         this.el.select('a', true).first().on('click', this.onClick, this);
29006         
29007     },
29008     
29009     onClick : function(e)
29010     {
29011         if(this.preventDefault){
29012             e.preventDefault();
29013         }
29014         
29015         this.fireEvent("click", this, e);
29016     },
29017     
29018     onMouseOver : function(e)
29019     {
29020         if(this.submenu && this.pos == 'left'){
29021             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29022         }
29023         
29024         this.fireEvent("mouseover", this, e);
29025     },
29026     
29027     onMouseOut : function(e)
29028     {
29029         this.fireEvent("mouseout", this, e);
29030     }
29031 });
29032
29033  
29034
29035  /*
29036  * - LGPL
29037  *
29038  * menu separator
29039  * 
29040  */
29041 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29042
29043 /**
29044  * @class Roo.bootstrap.menu.Separator
29045  * @extends Roo.bootstrap.Component
29046  * Bootstrap Separator class
29047  * 
29048  * @constructor
29049  * Create a new Separator
29050  * @param {Object} config The config object
29051  */
29052
29053
29054 Roo.bootstrap.menu.Separator = function(config){
29055     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29056 };
29057
29058 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29059     
29060     getAutoCreate : function(){
29061         var cfg = {
29062             tag : 'li',
29063             cls: 'dropdown-divider divider'
29064         };
29065         
29066         return cfg;
29067     }
29068    
29069 });
29070
29071  
29072
29073  /*
29074  * - LGPL
29075  *
29076  * Tooltip
29077  * 
29078  */
29079
29080 /**
29081  * @class Roo.bootstrap.Tooltip
29082  * Bootstrap Tooltip class
29083  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29084  * to determine which dom element triggers the tooltip.
29085  * 
29086  * It needs to add support for additional attributes like tooltip-position
29087  * 
29088  * @constructor
29089  * Create a new Toolti
29090  * @param {Object} config The config object
29091  */
29092
29093 Roo.bootstrap.Tooltip = function(config){
29094     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29095     
29096     this.alignment = Roo.bootstrap.Tooltip.alignment;
29097     
29098     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29099         this.alignment = config.alignment;
29100     }
29101     
29102 };
29103
29104 Roo.apply(Roo.bootstrap.Tooltip, {
29105     /**
29106      * @function init initialize tooltip monitoring.
29107      * @static
29108      */
29109     currentEl : false,
29110     currentTip : false,
29111     currentRegion : false,
29112     
29113     //  init : delay?
29114     
29115     init : function()
29116     {
29117         Roo.get(document).on('mouseover', this.enter ,this);
29118         Roo.get(document).on('mouseout', this.leave, this);
29119          
29120         
29121         this.currentTip = new Roo.bootstrap.Tooltip();
29122     },
29123     
29124     enter : function(ev)
29125     {
29126         var dom = ev.getTarget();
29127         
29128         //Roo.log(['enter',dom]);
29129         var el = Roo.fly(dom);
29130         if (this.currentEl) {
29131             //Roo.log(dom);
29132             //Roo.log(this.currentEl);
29133             //Roo.log(this.currentEl.contains(dom));
29134             if (this.currentEl == el) {
29135                 return;
29136             }
29137             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29138                 return;
29139             }
29140
29141         }
29142         
29143         if (this.currentTip.el) {
29144             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29145         }    
29146         //Roo.log(ev);
29147         
29148         if(!el || el.dom == document){
29149             return;
29150         }
29151         
29152         var bindEl = el; 
29153         var pel = false;
29154         if (!el.attr('tooltip')) {
29155             pel = el.findParent("[tooltip]");
29156             if (pel) {
29157                 bindEl = Roo.get(pel);
29158             }
29159         }
29160         
29161        
29162         
29163         // you can not look for children, as if el is the body.. then everythign is the child..
29164         if (!pel && !el.attr('tooltip')) { //
29165             if (!el.select("[tooltip]").elements.length) {
29166                 return;
29167             }
29168             // is the mouse over this child...?
29169             bindEl = el.select("[tooltip]").first();
29170             var xy = ev.getXY();
29171             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29172                 //Roo.log("not in region.");
29173                 return;
29174             }
29175             //Roo.log("child element over..");
29176             
29177         }
29178         this.currentEl = el;
29179         this.currentTip.bind(bindEl);
29180         this.currentRegion = Roo.lib.Region.getRegion(dom);
29181         this.currentTip.enter();
29182         
29183     },
29184     leave : function(ev)
29185     {
29186         var dom = ev.getTarget();
29187         //Roo.log(['leave',dom]);
29188         if (!this.currentEl) {
29189             return;
29190         }
29191         
29192         
29193         if (dom != this.currentEl.dom) {
29194             return;
29195         }
29196         var xy = ev.getXY();
29197         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29198             return;
29199         }
29200         // only activate leave if mouse cursor is outside... bounding box..
29201         
29202         
29203         
29204         
29205         if (this.currentTip) {
29206             this.currentTip.leave();
29207         }
29208         //Roo.log('clear currentEl');
29209         this.currentEl = false;
29210         
29211         
29212     },
29213     alignment : {
29214         'left' : ['r-l', [-2,0], 'right'],
29215         'right' : ['l-r', [2,0], 'left'],
29216         'bottom' : ['t-b', [0,2], 'top'],
29217         'top' : [ 'b-t', [0,-2], 'bottom']
29218     }
29219     
29220 });
29221
29222
29223 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29224     
29225     
29226     bindEl : false,
29227     
29228     delay : null, // can be { show : 300 , hide: 500}
29229     
29230     timeout : null,
29231     
29232     hoverState : null, //???
29233     
29234     placement : 'bottom', 
29235     
29236     alignment : false,
29237     
29238     getAutoCreate : function(){
29239     
29240         var cfg = {
29241            cls : 'tooltip',   
29242            role : 'tooltip',
29243            cn : [
29244                 {
29245                     cls : 'tooltip-arrow arrow'
29246                 },
29247                 {
29248                     cls : 'tooltip-inner'
29249                 }
29250            ]
29251         };
29252         
29253         return cfg;
29254     },
29255     bind : function(el)
29256     {
29257         this.bindEl = el;
29258     },
29259     
29260     initEvents : function()
29261     {
29262         this.arrowEl = this.el.select('.arrow', true).first();
29263         this.innerEl = this.el.select('.tooltip-inner', true).first();
29264     },
29265     
29266     enter : function () {
29267        
29268         if (this.timeout != null) {
29269             clearTimeout(this.timeout);
29270         }
29271         
29272         this.hoverState = 'in';
29273          //Roo.log("enter - show");
29274         if (!this.delay || !this.delay.show) {
29275             this.show();
29276             return;
29277         }
29278         var _t = this;
29279         this.timeout = setTimeout(function () {
29280             if (_t.hoverState == 'in') {
29281                 _t.show();
29282             }
29283         }, this.delay.show);
29284     },
29285     leave : function()
29286     {
29287         clearTimeout(this.timeout);
29288     
29289         this.hoverState = 'out';
29290          if (!this.delay || !this.delay.hide) {
29291             this.hide();
29292             return;
29293         }
29294        
29295         var _t = this;
29296         this.timeout = setTimeout(function () {
29297             //Roo.log("leave - timeout");
29298             
29299             if (_t.hoverState == 'out') {
29300                 _t.hide();
29301                 Roo.bootstrap.Tooltip.currentEl = false;
29302             }
29303         }, delay);
29304     },
29305     
29306     show : function (msg)
29307     {
29308         if (!this.el) {
29309             this.render(document.body);
29310         }
29311         // set content.
29312         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29313         
29314         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29315         
29316         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29317         
29318         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29319                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29320         
29321         var placement = typeof this.placement == 'function' ?
29322             this.placement.call(this, this.el, on_el) :
29323             this.placement;
29324             
29325         var autoToken = /\s?auto?\s?/i;
29326         var autoPlace = autoToken.test(placement);
29327         if (autoPlace) {
29328             placement = placement.replace(autoToken, '') || 'top';
29329         }
29330         
29331         //this.el.detach()
29332         //this.el.setXY([0,0]);
29333         this.el.show();
29334         //this.el.dom.style.display='block';
29335         
29336         //this.el.appendTo(on_el);
29337         
29338         var p = this.getPosition();
29339         var box = this.el.getBox();
29340         
29341         if (autoPlace) {
29342             // fixme..
29343         }
29344         
29345         var align = this.alignment[placement];
29346         
29347         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29348         
29349         if(placement == 'top' || placement == 'bottom'){
29350             if(xy[0] < 0){
29351                 placement = 'right';
29352             }
29353             
29354             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29355                 placement = 'left';
29356             }
29357             
29358             var scroll = Roo.select('body', true).first().getScroll();
29359             
29360             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29361                 placement = 'top';
29362             }
29363             
29364             align = this.alignment[placement];
29365             
29366             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29367             
29368         }
29369         
29370         var elems = document.getElementsByTagName('div');
29371         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29372         for (var i = 0; i < elems.length; i++) {
29373           var zindex = Number.parseInt(
29374                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29375                 10
29376           );
29377           if (zindex > highest) {
29378             highest = zindex;
29379           }
29380         }
29381         
29382         
29383         
29384         this.el.dom.style.zIndex = highest;
29385         
29386         this.el.alignTo(this.bindEl, align[0],align[1]);
29387         //var arrow = this.el.select('.arrow',true).first();
29388         //arrow.set(align[2], 
29389         
29390         this.el.addClass(placement);
29391         this.el.addClass("bs-tooltip-"+ placement);
29392         
29393         this.el.addClass('in fade show');
29394         
29395         this.hoverState = null;
29396         
29397         if (this.el.hasClass('fade')) {
29398             // fade it?
29399         }
29400         
29401         
29402         
29403         
29404         
29405     },
29406     hide : function()
29407     {
29408          
29409         if (!this.el) {
29410             return;
29411         }
29412         //this.el.setXY([0,0]);
29413         this.el.removeClass(['show', 'in']);
29414         //this.el.hide();
29415         
29416     }
29417     
29418 });
29419  
29420
29421  /*
29422  * - LGPL
29423  *
29424  * Location Picker
29425  * 
29426  */
29427
29428 /**
29429  * @class Roo.bootstrap.LocationPicker
29430  * @extends Roo.bootstrap.Component
29431  * Bootstrap LocationPicker class
29432  * @cfg {Number} latitude Position when init default 0
29433  * @cfg {Number} longitude Position when init default 0
29434  * @cfg {Number} zoom default 15
29435  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29436  * @cfg {Boolean} mapTypeControl default false
29437  * @cfg {Boolean} disableDoubleClickZoom default false
29438  * @cfg {Boolean} scrollwheel default true
29439  * @cfg {Boolean} streetViewControl default false
29440  * @cfg {Number} radius default 0
29441  * @cfg {String} locationName
29442  * @cfg {Boolean} draggable default true
29443  * @cfg {Boolean} enableAutocomplete default false
29444  * @cfg {Boolean} enableReverseGeocode default true
29445  * @cfg {String} markerTitle
29446  * 
29447  * @constructor
29448  * Create a new LocationPicker
29449  * @param {Object} config The config object
29450  */
29451
29452
29453 Roo.bootstrap.LocationPicker = function(config){
29454     
29455     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29456     
29457     this.addEvents({
29458         /**
29459          * @event initial
29460          * Fires when the picker initialized.
29461          * @param {Roo.bootstrap.LocationPicker} this
29462          * @param {Google Location} location
29463          */
29464         initial : true,
29465         /**
29466          * @event positionchanged
29467          * Fires when the picker position changed.
29468          * @param {Roo.bootstrap.LocationPicker} this
29469          * @param {Google Location} location
29470          */
29471         positionchanged : true,
29472         /**
29473          * @event resize
29474          * Fires when the map resize.
29475          * @param {Roo.bootstrap.LocationPicker} this
29476          */
29477         resize : true,
29478         /**
29479          * @event show
29480          * Fires when the map show.
29481          * @param {Roo.bootstrap.LocationPicker} this
29482          */
29483         show : true,
29484         /**
29485          * @event hide
29486          * Fires when the map hide.
29487          * @param {Roo.bootstrap.LocationPicker} this
29488          */
29489         hide : true,
29490         /**
29491          * @event mapClick
29492          * Fires when click the map.
29493          * @param {Roo.bootstrap.LocationPicker} this
29494          * @param {Map event} e
29495          */
29496         mapClick : true,
29497         /**
29498          * @event mapRightClick
29499          * Fires when right click the map.
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          * @param {Map event} e
29502          */
29503         mapRightClick : true,
29504         /**
29505          * @event markerClick
29506          * Fires when click the marker.
29507          * @param {Roo.bootstrap.LocationPicker} this
29508          * @param {Map event} e
29509          */
29510         markerClick : true,
29511         /**
29512          * @event markerRightClick
29513          * Fires when right click the marker.
29514          * @param {Roo.bootstrap.LocationPicker} this
29515          * @param {Map event} e
29516          */
29517         markerRightClick : true,
29518         /**
29519          * @event OverlayViewDraw
29520          * Fires when OverlayView Draw
29521          * @param {Roo.bootstrap.LocationPicker} this
29522          */
29523         OverlayViewDraw : true,
29524         /**
29525          * @event OverlayViewOnAdd
29526          * Fires when OverlayView Draw
29527          * @param {Roo.bootstrap.LocationPicker} this
29528          */
29529         OverlayViewOnAdd : true,
29530         /**
29531          * @event OverlayViewOnRemove
29532          * Fires when OverlayView Draw
29533          * @param {Roo.bootstrap.LocationPicker} this
29534          */
29535         OverlayViewOnRemove : true,
29536         /**
29537          * @event OverlayViewShow
29538          * Fires when OverlayView Draw
29539          * @param {Roo.bootstrap.LocationPicker} this
29540          * @param {Pixel} cpx
29541          */
29542         OverlayViewShow : true,
29543         /**
29544          * @event OverlayViewHide
29545          * Fires when OverlayView Draw
29546          * @param {Roo.bootstrap.LocationPicker} this
29547          */
29548         OverlayViewHide : true,
29549         /**
29550          * @event loadexception
29551          * Fires when load google lib failed.
29552          * @param {Roo.bootstrap.LocationPicker} this
29553          */
29554         loadexception : true
29555     });
29556         
29557 };
29558
29559 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29560     
29561     gMapContext: false,
29562     
29563     latitude: 0,
29564     longitude: 0,
29565     zoom: 15,
29566     mapTypeId: false,
29567     mapTypeControl: false,
29568     disableDoubleClickZoom: false,
29569     scrollwheel: true,
29570     streetViewControl: false,
29571     radius: 0,
29572     locationName: '',
29573     draggable: true,
29574     enableAutocomplete: false,
29575     enableReverseGeocode: true,
29576     markerTitle: '',
29577     
29578     getAutoCreate: function()
29579     {
29580
29581         var cfg = {
29582             tag: 'div',
29583             cls: 'roo-location-picker'
29584         };
29585         
29586         return cfg
29587     },
29588     
29589     initEvents: function(ct, position)
29590     {       
29591         if(!this.el.getWidth() || this.isApplied()){
29592             return;
29593         }
29594         
29595         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29596         
29597         this.initial();
29598     },
29599     
29600     initial: function()
29601     {
29602         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29603             this.fireEvent('loadexception', this);
29604             return;
29605         }
29606         
29607         if(!this.mapTypeId){
29608             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29609         }
29610         
29611         this.gMapContext = this.GMapContext();
29612         
29613         this.initOverlayView();
29614         
29615         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29616         
29617         var _this = this;
29618                 
29619         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29620             _this.setPosition(_this.gMapContext.marker.position);
29621         });
29622         
29623         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29624             _this.fireEvent('mapClick', this, event);
29625             
29626         });
29627
29628         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29629             _this.fireEvent('mapRightClick', this, event);
29630             
29631         });
29632         
29633         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29634             _this.fireEvent('markerClick', this, event);
29635             
29636         });
29637
29638         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29639             _this.fireEvent('markerRightClick', this, event);
29640             
29641         });
29642         
29643         this.setPosition(this.gMapContext.location);
29644         
29645         this.fireEvent('initial', this, this.gMapContext.location);
29646     },
29647     
29648     initOverlayView: function()
29649     {
29650         var _this = this;
29651         
29652         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29653             
29654             draw: function()
29655             {
29656                 _this.fireEvent('OverlayViewDraw', _this);
29657             },
29658             
29659             onAdd: function()
29660             {
29661                 _this.fireEvent('OverlayViewOnAdd', _this);
29662             },
29663             
29664             onRemove: function()
29665             {
29666                 _this.fireEvent('OverlayViewOnRemove', _this);
29667             },
29668             
29669             show: function(cpx)
29670             {
29671                 _this.fireEvent('OverlayViewShow', _this, cpx);
29672             },
29673             
29674             hide: function()
29675             {
29676                 _this.fireEvent('OverlayViewHide', _this);
29677             }
29678             
29679         });
29680     },
29681     
29682     fromLatLngToContainerPixel: function(event)
29683     {
29684         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29685     },
29686     
29687     isApplied: function() 
29688     {
29689         return this.getGmapContext() == false ? false : true;
29690     },
29691     
29692     getGmapContext: function() 
29693     {
29694         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29695     },
29696     
29697     GMapContext: function() 
29698     {
29699         var position = new google.maps.LatLng(this.latitude, this.longitude);
29700         
29701         var _map = new google.maps.Map(this.el.dom, {
29702             center: position,
29703             zoom: this.zoom,
29704             mapTypeId: this.mapTypeId,
29705             mapTypeControl: this.mapTypeControl,
29706             disableDoubleClickZoom: this.disableDoubleClickZoom,
29707             scrollwheel: this.scrollwheel,
29708             streetViewControl: this.streetViewControl,
29709             locationName: this.locationName,
29710             draggable: this.draggable,
29711             enableAutocomplete: this.enableAutocomplete,
29712             enableReverseGeocode: this.enableReverseGeocode
29713         });
29714         
29715         var _marker = new google.maps.Marker({
29716             position: position,
29717             map: _map,
29718             title: this.markerTitle,
29719             draggable: this.draggable
29720         });
29721         
29722         return {
29723             map: _map,
29724             marker: _marker,
29725             circle: null,
29726             location: position,
29727             radius: this.radius,
29728             locationName: this.locationName,
29729             addressComponents: {
29730                 formatted_address: null,
29731                 addressLine1: null,
29732                 addressLine2: null,
29733                 streetName: null,
29734                 streetNumber: null,
29735                 city: null,
29736                 district: null,
29737                 state: null,
29738                 stateOrProvince: null
29739             },
29740             settings: this,
29741             domContainer: this.el.dom,
29742             geodecoder: new google.maps.Geocoder()
29743         };
29744     },
29745     
29746     drawCircle: function(center, radius, options) 
29747     {
29748         if (this.gMapContext.circle != null) {
29749             this.gMapContext.circle.setMap(null);
29750         }
29751         if (radius > 0) {
29752             radius *= 1;
29753             options = Roo.apply({}, options, {
29754                 strokeColor: "#0000FF",
29755                 strokeOpacity: .35,
29756                 strokeWeight: 2,
29757                 fillColor: "#0000FF",
29758                 fillOpacity: .2
29759             });
29760             
29761             options.map = this.gMapContext.map;
29762             options.radius = radius;
29763             options.center = center;
29764             this.gMapContext.circle = new google.maps.Circle(options);
29765             return this.gMapContext.circle;
29766         }
29767         
29768         return null;
29769     },
29770     
29771     setPosition: function(location) 
29772     {
29773         this.gMapContext.location = location;
29774         this.gMapContext.marker.setPosition(location);
29775         this.gMapContext.map.panTo(location);
29776         this.drawCircle(location, this.gMapContext.radius, {});
29777         
29778         var _this = this;
29779         
29780         if (this.gMapContext.settings.enableReverseGeocode) {
29781             this.gMapContext.geodecoder.geocode({
29782                 latLng: this.gMapContext.location
29783             }, function(results, status) {
29784                 
29785                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29786                     _this.gMapContext.locationName = results[0].formatted_address;
29787                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29788                     
29789                     _this.fireEvent('positionchanged', this, location);
29790                 }
29791             });
29792             
29793             return;
29794         }
29795         
29796         this.fireEvent('positionchanged', this, location);
29797     },
29798     
29799     resize: function()
29800     {
29801         google.maps.event.trigger(this.gMapContext.map, "resize");
29802         
29803         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29804         
29805         this.fireEvent('resize', this);
29806     },
29807     
29808     setPositionByLatLng: function(latitude, longitude)
29809     {
29810         this.setPosition(new google.maps.LatLng(latitude, longitude));
29811     },
29812     
29813     getCurrentPosition: function() 
29814     {
29815         return {
29816             latitude: this.gMapContext.location.lat(),
29817             longitude: this.gMapContext.location.lng()
29818         };
29819     },
29820     
29821     getAddressName: function() 
29822     {
29823         return this.gMapContext.locationName;
29824     },
29825     
29826     getAddressComponents: function() 
29827     {
29828         return this.gMapContext.addressComponents;
29829     },
29830     
29831     address_component_from_google_geocode: function(address_components) 
29832     {
29833         var result = {};
29834         
29835         for (var i = 0; i < address_components.length; i++) {
29836             var component = address_components[i];
29837             if (component.types.indexOf("postal_code") >= 0) {
29838                 result.postalCode = component.short_name;
29839             } else if (component.types.indexOf("street_number") >= 0) {
29840                 result.streetNumber = component.short_name;
29841             } else if (component.types.indexOf("route") >= 0) {
29842                 result.streetName = component.short_name;
29843             } else if (component.types.indexOf("neighborhood") >= 0) {
29844                 result.city = component.short_name;
29845             } else if (component.types.indexOf("locality") >= 0) {
29846                 result.city = component.short_name;
29847             } else if (component.types.indexOf("sublocality") >= 0) {
29848                 result.district = component.short_name;
29849             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29850                 result.stateOrProvince = component.short_name;
29851             } else if (component.types.indexOf("country") >= 0) {
29852                 result.country = component.short_name;
29853             }
29854         }
29855         
29856         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29857         result.addressLine2 = "";
29858         return result;
29859     },
29860     
29861     setZoomLevel: function(zoom)
29862     {
29863         this.gMapContext.map.setZoom(zoom);
29864     },
29865     
29866     show: function()
29867     {
29868         if(!this.el){
29869             return;
29870         }
29871         
29872         this.el.show();
29873         
29874         this.resize();
29875         
29876         this.fireEvent('show', this);
29877     },
29878     
29879     hide: function()
29880     {
29881         if(!this.el){
29882             return;
29883         }
29884         
29885         this.el.hide();
29886         
29887         this.fireEvent('hide', this);
29888     }
29889     
29890 });
29891
29892 Roo.apply(Roo.bootstrap.LocationPicker, {
29893     
29894     OverlayView : function(map, options)
29895     {
29896         options = options || {};
29897         
29898         this.setMap(map);
29899     }
29900     
29901     
29902 });/**
29903  * @class Roo.bootstrap.Alert
29904  * @extends Roo.bootstrap.Component
29905  * Bootstrap Alert class - shows an alert area box
29906  * eg
29907  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29908   Enter a valid email address
29909 </div>
29910  * @licence LGPL
29911  * @cfg {String} title The title of alert
29912  * @cfg {String} html The content of alert
29913  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29914  * @cfg {String} fa font-awesomeicon
29915  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29916  * @cfg {Boolean} close true to show a x closer
29917  * 
29918  * 
29919  * @constructor
29920  * Create a new alert
29921  * @param {Object} config The config object
29922  */
29923
29924
29925 Roo.bootstrap.Alert = function(config){
29926     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29927     
29928 };
29929
29930 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29931     
29932     title: '',
29933     html: '',
29934     weight: false,
29935     fa: false,
29936     faicon: false, // BC
29937     close : false,
29938     
29939     
29940     getAutoCreate : function()
29941     {
29942         
29943         var cfg = {
29944             tag : 'div',
29945             cls : 'alert',
29946             cn : [
29947                 {
29948                     tag: 'button',
29949                     type :  "button",
29950                     cls: "close",
29951                     html : '×',
29952                     style : this.close ? '' : 'display:none'
29953                 },
29954                 {
29955                     tag : 'i',
29956                     cls : 'roo-alert-icon'
29957                     
29958                 },
29959                 {
29960                     tag : 'b',
29961                     cls : 'roo-alert-title',
29962                     html : this.title
29963                 },
29964                 {
29965                     tag : 'span',
29966                     cls : 'roo-alert-text',
29967                     html : this.html
29968                 }
29969             ]
29970         };
29971         
29972         if(this.faicon){
29973             cfg.cn[0].cls += ' fa ' + this.faicon;
29974         }
29975         if(this.fa){
29976             cfg.cn[0].cls += ' fa ' + this.fa;
29977         }
29978         
29979         if(this.weight){
29980             cfg.cls += ' alert-' + this.weight;
29981         }
29982         
29983         return cfg;
29984     },
29985     
29986     initEvents: function() 
29987     {
29988         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29989         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29990         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29991         this.htmlEl = this.el.select('.roo-alert-text',true).first();
29992         if (this.seconds > 0) {
29993             this.hide.defer(this.seconds, this);
29994         }
29995     },
29996     /**
29997      * Set the Title Message HTML
29998      * @param {String} html
29999      */
30000     setTitle : function(str)
30001     {
30002         this.titleEl.dom.innerHTML = str;
30003     },
30004      
30005      /**
30006      * Set the Body Message HTML
30007      * @param {String} html
30008      */
30009     setHtml : function(str)
30010     {
30011         this.htmlEl.dom.innerHTML = str;
30012     },
30013     /**
30014      * Set the Weight of the alert
30015      * @param {String} (success|info|warning|danger) weight
30016      */
30017     
30018     setWeight : function(weight)
30019     {
30020         if(this.weight){
30021             this.el.removeClass('alert-' + this.weight);
30022         }
30023         
30024         this.weight = weight;
30025         
30026         this.el.addClass('alert-' + this.weight);
30027     },
30028       /**
30029      * Set the Icon of the alert
30030      * @param {String} see fontawsome names (name without the 'fa-' bit)
30031      */
30032     setIcon : function(icon)
30033     {
30034         if(this.faicon){
30035             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30036         }
30037         
30038         this.faicon = icon;
30039         
30040         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30041     },
30042     /**
30043      * Hide the Alert
30044      */
30045     hide: function() 
30046     {
30047         this.el.hide();   
30048     },
30049     /**
30050      * Show the Alert
30051      */
30052     show: function() 
30053     {  
30054         this.el.show();   
30055     }
30056     
30057 });
30058
30059  
30060 /*
30061 * Licence: LGPL
30062 */
30063
30064 /**
30065  * @class Roo.bootstrap.UploadCropbox
30066  * @extends Roo.bootstrap.Component
30067  * Bootstrap UploadCropbox class
30068  * @cfg {String} emptyText show when image has been loaded
30069  * @cfg {String} rotateNotify show when image too small to rotate
30070  * @cfg {Number} errorTimeout default 3000
30071  * @cfg {Number} minWidth default 300
30072  * @cfg {Number} minHeight default 300
30073  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30074  * @cfg {Boolean} isDocument (true|false) default false
30075  * @cfg {String} url action url
30076  * @cfg {String} paramName default 'imageUpload'
30077  * @cfg {String} method default POST
30078  * @cfg {Boolean} loadMask (true|false) default true
30079  * @cfg {Boolean} loadingText default 'Loading...'
30080  * 
30081  * @constructor
30082  * Create a new UploadCropbox
30083  * @param {Object} config The config object
30084  */
30085
30086 Roo.bootstrap.UploadCropbox = function(config){
30087     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30088     
30089     this.addEvents({
30090         /**
30091          * @event beforeselectfile
30092          * Fire before select file
30093          * @param {Roo.bootstrap.UploadCropbox} this
30094          */
30095         "beforeselectfile" : true,
30096         /**
30097          * @event initial
30098          * Fire after initEvent
30099          * @param {Roo.bootstrap.UploadCropbox} this
30100          */
30101         "initial" : true,
30102         /**
30103          * @event crop
30104          * Fire after initEvent
30105          * @param {Roo.bootstrap.UploadCropbox} this
30106          * @param {String} data
30107          */
30108         "crop" : true,
30109         /**
30110          * @event prepare
30111          * Fire when preparing the file data
30112          * @param {Roo.bootstrap.UploadCropbox} this
30113          * @param {Object} file
30114          */
30115         "prepare" : true,
30116         /**
30117          * @event exception
30118          * Fire when get exception
30119          * @param {Roo.bootstrap.UploadCropbox} this
30120          * @param {XMLHttpRequest} xhr
30121          */
30122         "exception" : true,
30123         /**
30124          * @event beforeloadcanvas
30125          * Fire before load the canvas
30126          * @param {Roo.bootstrap.UploadCropbox} this
30127          * @param {String} src
30128          */
30129         "beforeloadcanvas" : true,
30130         /**
30131          * @event trash
30132          * Fire when trash image
30133          * @param {Roo.bootstrap.UploadCropbox} this
30134          */
30135         "trash" : true,
30136         /**
30137          * @event download
30138          * Fire when download the image
30139          * @param {Roo.bootstrap.UploadCropbox} this
30140          */
30141         "download" : true,
30142         /**
30143          * @event footerbuttonclick
30144          * Fire when footerbuttonclick
30145          * @param {Roo.bootstrap.UploadCropbox} this
30146          * @param {String} type
30147          */
30148         "footerbuttonclick" : true,
30149         /**
30150          * @event resize
30151          * Fire when resize
30152          * @param {Roo.bootstrap.UploadCropbox} this
30153          */
30154         "resize" : true,
30155         /**
30156          * @event rotate
30157          * Fire when rotate the image
30158          * @param {Roo.bootstrap.UploadCropbox} this
30159          * @param {String} pos
30160          */
30161         "rotate" : true,
30162         /**
30163          * @event inspect
30164          * Fire when inspect the file
30165          * @param {Roo.bootstrap.UploadCropbox} this
30166          * @param {Object} file
30167          */
30168         "inspect" : true,
30169         /**
30170          * @event upload
30171          * Fire when xhr upload the file
30172          * @param {Roo.bootstrap.UploadCropbox} this
30173          * @param {Object} data
30174          */
30175         "upload" : true,
30176         /**
30177          * @event arrange
30178          * Fire when arrange the file data
30179          * @param {Roo.bootstrap.UploadCropbox} this
30180          * @param {Object} formData
30181          */
30182         "arrange" : true
30183     });
30184     
30185     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30186 };
30187
30188 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30189     
30190     emptyText : 'Click to upload image',
30191     rotateNotify : 'Image is too small to rotate',
30192     errorTimeout : 3000,
30193     scale : 0,
30194     baseScale : 1,
30195     rotate : 0,
30196     dragable : false,
30197     pinching : false,
30198     mouseX : 0,
30199     mouseY : 0,
30200     cropData : false,
30201     minWidth : 300,
30202     minHeight : 300,
30203     file : false,
30204     exif : {},
30205     baseRotate : 1,
30206     cropType : 'image/jpeg',
30207     buttons : false,
30208     canvasLoaded : false,
30209     isDocument : false,
30210     method : 'POST',
30211     paramName : 'imageUpload',
30212     loadMask : true,
30213     loadingText : 'Loading...',
30214     maskEl : false,
30215     
30216     getAutoCreate : function()
30217     {
30218         var cfg = {
30219             tag : 'div',
30220             cls : 'roo-upload-cropbox',
30221             cn : [
30222                 {
30223                     tag : 'input',
30224                     cls : 'roo-upload-cropbox-selector',
30225                     type : 'file'
30226                 },
30227                 {
30228                     tag : 'div',
30229                     cls : 'roo-upload-cropbox-body',
30230                     style : 'cursor:pointer',
30231                     cn : [
30232                         {
30233                             tag : 'div',
30234                             cls : 'roo-upload-cropbox-preview'
30235                         },
30236                         {
30237                             tag : 'div',
30238                             cls : 'roo-upload-cropbox-thumb'
30239                         },
30240                         {
30241                             tag : 'div',
30242                             cls : 'roo-upload-cropbox-empty-notify',
30243                             html : this.emptyText
30244                         },
30245                         {
30246                             tag : 'div',
30247                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30248                             html : this.rotateNotify
30249                         }
30250                     ]
30251                 },
30252                 {
30253                     tag : 'div',
30254                     cls : 'roo-upload-cropbox-footer',
30255                     cn : {
30256                         tag : 'div',
30257                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30258                         cn : []
30259                     }
30260                 }
30261             ]
30262         };
30263         
30264         return cfg;
30265     },
30266     
30267     onRender : function(ct, position)
30268     {
30269         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30270         
30271         if (this.buttons.length) {
30272             
30273             Roo.each(this.buttons, function(bb) {
30274                 
30275                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30276                 
30277                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30278                 
30279             }, this);
30280         }
30281         
30282         if(this.loadMask){
30283             this.maskEl = this.el;
30284         }
30285     },
30286     
30287     initEvents : function()
30288     {
30289         this.urlAPI = (window.createObjectURL && window) || 
30290                                 (window.URL && URL.revokeObjectURL && URL) || 
30291                                 (window.webkitURL && webkitURL);
30292                         
30293         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30294         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30295         
30296         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30297         this.selectorEl.hide();
30298         
30299         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30300         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30301         
30302         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30303         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30304         this.thumbEl.hide();
30305         
30306         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30307         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30308         
30309         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30310         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30311         this.errorEl.hide();
30312         
30313         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30314         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30315         this.footerEl.hide();
30316         
30317         this.setThumbBoxSize();
30318         
30319         this.bind();
30320         
30321         this.resize();
30322         
30323         this.fireEvent('initial', this);
30324     },
30325
30326     bind : function()
30327     {
30328         var _this = this;
30329         
30330         window.addEventListener("resize", function() { _this.resize(); } );
30331         
30332         this.bodyEl.on('click', this.beforeSelectFile, this);
30333         
30334         if(Roo.isTouch){
30335             this.bodyEl.on('touchstart', this.onTouchStart, this);
30336             this.bodyEl.on('touchmove', this.onTouchMove, this);
30337             this.bodyEl.on('touchend', this.onTouchEnd, this);
30338         }
30339         
30340         if(!Roo.isTouch){
30341             this.bodyEl.on('mousedown', this.onMouseDown, this);
30342             this.bodyEl.on('mousemove', this.onMouseMove, this);
30343             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30344             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30345             Roo.get(document).on('mouseup', this.onMouseUp, this);
30346         }
30347         
30348         this.selectorEl.on('change', this.onFileSelected, this);
30349     },
30350     
30351     reset : function()
30352     {    
30353         this.scale = 0;
30354         this.baseScale = 1;
30355         this.rotate = 0;
30356         this.baseRotate = 1;
30357         this.dragable = false;
30358         this.pinching = false;
30359         this.mouseX = 0;
30360         this.mouseY = 0;
30361         this.cropData = false;
30362         this.notifyEl.dom.innerHTML = this.emptyText;
30363         
30364         this.selectorEl.dom.value = '';
30365         
30366     },
30367     
30368     resize : function()
30369     {
30370         if(this.fireEvent('resize', this) != false){
30371             this.setThumbBoxPosition();
30372             this.setCanvasPosition();
30373         }
30374     },
30375     
30376     onFooterButtonClick : function(e, el, o, type)
30377     {
30378         switch (type) {
30379             case 'rotate-left' :
30380                 this.onRotateLeft(e);
30381                 break;
30382             case 'rotate-right' :
30383                 this.onRotateRight(e);
30384                 break;
30385             case 'picture' :
30386                 this.beforeSelectFile(e);
30387                 break;
30388             case 'trash' :
30389                 this.trash(e);
30390                 break;
30391             case 'crop' :
30392                 this.crop(e);
30393                 break;
30394             case 'download' :
30395                 this.download(e);
30396                 break;
30397             default :
30398                 break;
30399         }
30400         
30401         this.fireEvent('footerbuttonclick', this, type);
30402     },
30403     
30404     beforeSelectFile : function(e)
30405     {
30406         e.preventDefault();
30407         
30408         if(this.fireEvent('beforeselectfile', this) != false){
30409             this.selectorEl.dom.click();
30410         }
30411     },
30412     
30413     onFileSelected : function(e)
30414     {
30415         e.preventDefault();
30416         
30417         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30418             return;
30419         }
30420         
30421         var file = this.selectorEl.dom.files[0];
30422         
30423         if(this.fireEvent('inspect', this, file) != false){
30424             this.prepare(file);
30425         }
30426         
30427     },
30428     
30429     trash : function(e)
30430     {
30431         this.fireEvent('trash', this);
30432     },
30433     
30434     download : function(e)
30435     {
30436         this.fireEvent('download', this);
30437     },
30438     
30439     loadCanvas : function(src)
30440     {   
30441         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30442             
30443             this.reset();
30444             
30445             this.imageEl = document.createElement('img');
30446             
30447             var _this = this;
30448             
30449             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30450             
30451             this.imageEl.src = src;
30452         }
30453     },
30454     
30455     onLoadCanvas : function()
30456     {   
30457         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30458         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30459         
30460         this.bodyEl.un('click', this.beforeSelectFile, this);
30461         
30462         this.notifyEl.hide();
30463         this.thumbEl.show();
30464         this.footerEl.show();
30465         
30466         this.baseRotateLevel();
30467         
30468         if(this.isDocument){
30469             this.setThumbBoxSize();
30470         }
30471         
30472         this.setThumbBoxPosition();
30473         
30474         this.baseScaleLevel();
30475         
30476         this.draw();
30477         
30478         this.resize();
30479         
30480         this.canvasLoaded = true;
30481         
30482         if(this.loadMask){
30483             this.maskEl.unmask();
30484         }
30485         
30486     },
30487     
30488     setCanvasPosition : function()
30489     {   
30490         if(!this.canvasEl){
30491             return;
30492         }
30493         
30494         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30495         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30496         
30497         this.previewEl.setLeft(pw);
30498         this.previewEl.setTop(ph);
30499         
30500     },
30501     
30502     onMouseDown : function(e)
30503     {   
30504         e.stopEvent();
30505         
30506         this.dragable = true;
30507         this.pinching = false;
30508         
30509         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30510             this.dragable = false;
30511             return;
30512         }
30513         
30514         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30515         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30516         
30517     },
30518     
30519     onMouseMove : function(e)
30520     {   
30521         e.stopEvent();
30522         
30523         if(!this.canvasLoaded){
30524             return;
30525         }
30526         
30527         if (!this.dragable){
30528             return;
30529         }
30530         
30531         var minX = Math.ceil(this.thumbEl.getLeft(true));
30532         var minY = Math.ceil(this.thumbEl.getTop(true));
30533         
30534         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30535         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30536         
30537         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30538         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30539         
30540         x = x - this.mouseX;
30541         y = y - this.mouseY;
30542         
30543         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30544         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30545         
30546         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30547         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30548         
30549         this.previewEl.setLeft(bgX);
30550         this.previewEl.setTop(bgY);
30551         
30552         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30553         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30554     },
30555     
30556     onMouseUp : function(e)
30557     {   
30558         e.stopEvent();
30559         
30560         this.dragable = false;
30561     },
30562     
30563     onMouseWheel : function(e)
30564     {   
30565         e.stopEvent();
30566         
30567         this.startScale = this.scale;
30568         
30569         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30570         
30571         if(!this.zoomable()){
30572             this.scale = this.startScale;
30573             return;
30574         }
30575         
30576         this.draw();
30577         
30578         return;
30579     },
30580     
30581     zoomable : function()
30582     {
30583         var minScale = this.thumbEl.getWidth() / this.minWidth;
30584         
30585         if(this.minWidth < this.minHeight){
30586             minScale = this.thumbEl.getHeight() / this.minHeight;
30587         }
30588         
30589         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30590         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30591         
30592         if(
30593                 this.isDocument &&
30594                 (this.rotate == 0 || this.rotate == 180) && 
30595                 (
30596                     width > this.imageEl.OriginWidth || 
30597                     height > this.imageEl.OriginHeight ||
30598                     (width < this.minWidth && height < this.minHeight)
30599                 )
30600         ){
30601             return false;
30602         }
30603         
30604         if(
30605                 this.isDocument &&
30606                 (this.rotate == 90 || this.rotate == 270) && 
30607                 (
30608                     width > this.imageEl.OriginWidth || 
30609                     height > this.imageEl.OriginHeight ||
30610                     (width < this.minHeight && height < this.minWidth)
30611                 )
30612         ){
30613             return false;
30614         }
30615         
30616         if(
30617                 !this.isDocument &&
30618                 (this.rotate == 0 || this.rotate == 180) && 
30619                 (
30620                     width < this.minWidth || 
30621                     width > this.imageEl.OriginWidth || 
30622                     height < this.minHeight || 
30623                     height > this.imageEl.OriginHeight
30624                 )
30625         ){
30626             return false;
30627         }
30628         
30629         if(
30630                 !this.isDocument &&
30631                 (this.rotate == 90 || this.rotate == 270) && 
30632                 (
30633                     width < this.minHeight || 
30634                     width > this.imageEl.OriginWidth || 
30635                     height < this.minWidth || 
30636                     height > this.imageEl.OriginHeight
30637                 )
30638         ){
30639             return false;
30640         }
30641         
30642         return true;
30643         
30644     },
30645     
30646     onRotateLeft : function(e)
30647     {   
30648         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30649             
30650             var minScale = this.thumbEl.getWidth() / this.minWidth;
30651             
30652             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30653             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30654             
30655             this.startScale = this.scale;
30656             
30657             while (this.getScaleLevel() < minScale){
30658             
30659                 this.scale = this.scale + 1;
30660                 
30661                 if(!this.zoomable()){
30662                     break;
30663                 }
30664                 
30665                 if(
30666                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30667                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30668                 ){
30669                     continue;
30670                 }
30671                 
30672                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30673
30674                 this.draw();
30675                 
30676                 return;
30677             }
30678             
30679             this.scale = this.startScale;
30680             
30681             this.onRotateFail();
30682             
30683             return false;
30684         }
30685         
30686         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30687
30688         if(this.isDocument){
30689             this.setThumbBoxSize();
30690             this.setThumbBoxPosition();
30691             this.setCanvasPosition();
30692         }
30693         
30694         this.draw();
30695         
30696         this.fireEvent('rotate', this, 'left');
30697         
30698     },
30699     
30700     onRotateRight : function(e)
30701     {
30702         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30703             
30704             var minScale = this.thumbEl.getWidth() / this.minWidth;
30705         
30706             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30707             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30708             
30709             this.startScale = this.scale;
30710             
30711             while (this.getScaleLevel() < minScale){
30712             
30713                 this.scale = this.scale + 1;
30714                 
30715                 if(!this.zoomable()){
30716                     break;
30717                 }
30718                 
30719                 if(
30720                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30721                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30722                 ){
30723                     continue;
30724                 }
30725                 
30726                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30727
30728                 this.draw();
30729                 
30730                 return;
30731             }
30732             
30733             this.scale = this.startScale;
30734             
30735             this.onRotateFail();
30736             
30737             return false;
30738         }
30739         
30740         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30741
30742         if(this.isDocument){
30743             this.setThumbBoxSize();
30744             this.setThumbBoxPosition();
30745             this.setCanvasPosition();
30746         }
30747         
30748         this.draw();
30749         
30750         this.fireEvent('rotate', this, 'right');
30751     },
30752     
30753     onRotateFail : function()
30754     {
30755         this.errorEl.show(true);
30756         
30757         var _this = this;
30758         
30759         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30760     },
30761     
30762     draw : function()
30763     {
30764         this.previewEl.dom.innerHTML = '';
30765         
30766         var canvasEl = document.createElement("canvas");
30767         
30768         var contextEl = canvasEl.getContext("2d");
30769         
30770         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30771         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30772         var center = this.imageEl.OriginWidth / 2;
30773         
30774         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30775             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30776             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30777             center = this.imageEl.OriginHeight / 2;
30778         }
30779         
30780         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30781         
30782         contextEl.translate(center, center);
30783         contextEl.rotate(this.rotate * Math.PI / 180);
30784
30785         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30786         
30787         this.canvasEl = document.createElement("canvas");
30788         
30789         this.contextEl = this.canvasEl.getContext("2d");
30790         
30791         switch (this.rotate) {
30792             case 0 :
30793                 
30794                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30795                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30796                 
30797                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30798                 
30799                 break;
30800             case 90 : 
30801                 
30802                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30803                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30804                 
30805                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30806                     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);
30807                     break;
30808                 }
30809                 
30810                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30811                 
30812                 break;
30813             case 180 :
30814                 
30815                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30816                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30817                 
30818                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30819                     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);
30820                     break;
30821                 }
30822                 
30823                 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);
30824                 
30825                 break;
30826             case 270 :
30827                 
30828                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30829                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30830         
30831                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30832                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30833                     break;
30834                 }
30835                 
30836                 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);
30837                 
30838                 break;
30839             default : 
30840                 break;
30841         }
30842         
30843         this.previewEl.appendChild(this.canvasEl);
30844         
30845         this.setCanvasPosition();
30846     },
30847     
30848     crop : function()
30849     {
30850         if(!this.canvasLoaded){
30851             return;
30852         }
30853         
30854         var imageCanvas = document.createElement("canvas");
30855         
30856         var imageContext = imageCanvas.getContext("2d");
30857         
30858         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30859         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30860         
30861         var center = imageCanvas.width / 2;
30862         
30863         imageContext.translate(center, center);
30864         
30865         imageContext.rotate(this.rotate * Math.PI / 180);
30866         
30867         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30868         
30869         var canvas = document.createElement("canvas");
30870         
30871         var context = canvas.getContext("2d");
30872                 
30873         canvas.width = this.minWidth;
30874         canvas.height = this.minHeight;
30875
30876         switch (this.rotate) {
30877             case 0 :
30878                 
30879                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30880                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30881                 
30882                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30883                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30884                 
30885                 var targetWidth = this.minWidth - 2 * x;
30886                 var targetHeight = this.minHeight - 2 * y;
30887                 
30888                 var scale = 1;
30889                 
30890                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30891                     scale = targetWidth / width;
30892                 }
30893                 
30894                 if(x > 0 && y == 0){
30895                     scale = targetHeight / height;
30896                 }
30897                 
30898                 if(x > 0 && y > 0){
30899                     scale = targetWidth / width;
30900                     
30901                     if(width < height){
30902                         scale = targetHeight / height;
30903                     }
30904                 }
30905                 
30906                 context.scale(scale, scale);
30907                 
30908                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30909                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30910
30911                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30912                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30913
30914                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30915                 
30916                 break;
30917             case 90 : 
30918                 
30919                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30920                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30921                 
30922                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30923                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30924                 
30925                 var targetWidth = this.minWidth - 2 * x;
30926                 var targetHeight = this.minHeight - 2 * y;
30927                 
30928                 var scale = 1;
30929                 
30930                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30931                     scale = targetWidth / width;
30932                 }
30933                 
30934                 if(x > 0 && y == 0){
30935                     scale = targetHeight / height;
30936                 }
30937                 
30938                 if(x > 0 && y > 0){
30939                     scale = targetWidth / width;
30940                     
30941                     if(width < height){
30942                         scale = targetHeight / height;
30943                     }
30944                 }
30945                 
30946                 context.scale(scale, scale);
30947                 
30948                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30949                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30950
30951                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30952                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30953                 
30954                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30955                 
30956                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30957                 
30958                 break;
30959             case 180 :
30960                 
30961                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30962                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30963                 
30964                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30965                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30966                 
30967                 var targetWidth = this.minWidth - 2 * x;
30968                 var targetHeight = this.minHeight - 2 * y;
30969                 
30970                 var scale = 1;
30971                 
30972                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30973                     scale = targetWidth / width;
30974                 }
30975                 
30976                 if(x > 0 && y == 0){
30977                     scale = targetHeight / height;
30978                 }
30979                 
30980                 if(x > 0 && y > 0){
30981                     scale = targetWidth / width;
30982                     
30983                     if(width < height){
30984                         scale = targetHeight / height;
30985                     }
30986                 }
30987                 
30988                 context.scale(scale, scale);
30989                 
30990                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30991                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30992
30993                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30994                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30995
30996                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30997                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30998                 
30999                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31000                 
31001                 break;
31002             case 270 :
31003                 
31004                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31005                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31006                 
31007                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31008                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31009                 
31010                 var targetWidth = this.minWidth - 2 * x;
31011                 var targetHeight = this.minHeight - 2 * y;
31012                 
31013                 var scale = 1;
31014                 
31015                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31016                     scale = targetWidth / width;
31017                 }
31018                 
31019                 if(x > 0 && y == 0){
31020                     scale = targetHeight / height;
31021                 }
31022                 
31023                 if(x > 0 && y > 0){
31024                     scale = targetWidth / width;
31025                     
31026                     if(width < height){
31027                         scale = targetHeight / height;
31028                     }
31029                 }
31030                 
31031                 context.scale(scale, scale);
31032                 
31033                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31034                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31035
31036                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31037                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31038                 
31039                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31040                 
31041                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31042                 
31043                 break;
31044             default : 
31045                 break;
31046         }
31047         
31048         this.cropData = canvas.toDataURL(this.cropType);
31049         
31050         if(this.fireEvent('crop', this, this.cropData) !== false){
31051             this.process(this.file, this.cropData);
31052         }
31053         
31054         return;
31055         
31056     },
31057     
31058     setThumbBoxSize : function()
31059     {
31060         var width, height;
31061         
31062         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31063             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31064             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31065             
31066             this.minWidth = width;
31067             this.minHeight = height;
31068             
31069             if(this.rotate == 90 || this.rotate == 270){
31070                 this.minWidth = height;
31071                 this.minHeight = width;
31072             }
31073         }
31074         
31075         height = 300;
31076         width = Math.ceil(this.minWidth * height / this.minHeight);
31077         
31078         if(this.minWidth > this.minHeight){
31079             width = 300;
31080             height = Math.ceil(this.minHeight * width / this.minWidth);
31081         }
31082         
31083         this.thumbEl.setStyle({
31084             width : width + 'px',
31085             height : height + 'px'
31086         });
31087
31088         return;
31089             
31090     },
31091     
31092     setThumbBoxPosition : function()
31093     {
31094         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31095         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31096         
31097         this.thumbEl.setLeft(x);
31098         this.thumbEl.setTop(y);
31099         
31100     },
31101     
31102     baseRotateLevel : function()
31103     {
31104         this.baseRotate = 1;
31105         
31106         if(
31107                 typeof(this.exif) != 'undefined' &&
31108                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31109                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31110         ){
31111             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31112         }
31113         
31114         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31115         
31116     },
31117     
31118     baseScaleLevel : function()
31119     {
31120         var width, height;
31121         
31122         if(this.isDocument){
31123             
31124             if(this.baseRotate == 6 || this.baseRotate == 8){
31125             
31126                 height = this.thumbEl.getHeight();
31127                 this.baseScale = height / this.imageEl.OriginWidth;
31128
31129                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31130                     width = this.thumbEl.getWidth();
31131                     this.baseScale = width / this.imageEl.OriginHeight;
31132                 }
31133
31134                 return;
31135             }
31136
31137             height = this.thumbEl.getHeight();
31138             this.baseScale = height / this.imageEl.OriginHeight;
31139
31140             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31141                 width = this.thumbEl.getWidth();
31142                 this.baseScale = width / this.imageEl.OriginWidth;
31143             }
31144
31145             return;
31146         }
31147         
31148         if(this.baseRotate == 6 || this.baseRotate == 8){
31149             
31150             width = this.thumbEl.getHeight();
31151             this.baseScale = width / this.imageEl.OriginHeight;
31152             
31153             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31154                 height = this.thumbEl.getWidth();
31155                 this.baseScale = height / this.imageEl.OriginHeight;
31156             }
31157             
31158             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31159                 height = this.thumbEl.getWidth();
31160                 this.baseScale = height / this.imageEl.OriginHeight;
31161                 
31162                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31163                     width = this.thumbEl.getHeight();
31164                     this.baseScale = width / this.imageEl.OriginWidth;
31165                 }
31166             }
31167             
31168             return;
31169         }
31170         
31171         width = this.thumbEl.getWidth();
31172         this.baseScale = width / this.imageEl.OriginWidth;
31173         
31174         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31175             height = this.thumbEl.getHeight();
31176             this.baseScale = height / this.imageEl.OriginHeight;
31177         }
31178         
31179         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31180             
31181             height = this.thumbEl.getHeight();
31182             this.baseScale = height / this.imageEl.OriginHeight;
31183             
31184             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31185                 width = this.thumbEl.getWidth();
31186                 this.baseScale = width / this.imageEl.OriginWidth;
31187             }
31188             
31189         }
31190         
31191         return;
31192     },
31193     
31194     getScaleLevel : function()
31195     {
31196         return this.baseScale * Math.pow(1.1, this.scale);
31197     },
31198     
31199     onTouchStart : function(e)
31200     {
31201         if(!this.canvasLoaded){
31202             this.beforeSelectFile(e);
31203             return;
31204         }
31205         
31206         var touches = e.browserEvent.touches;
31207         
31208         if(!touches){
31209             return;
31210         }
31211         
31212         if(touches.length == 1){
31213             this.onMouseDown(e);
31214             return;
31215         }
31216         
31217         if(touches.length != 2){
31218             return;
31219         }
31220         
31221         var coords = [];
31222         
31223         for(var i = 0, finger; finger = touches[i]; i++){
31224             coords.push(finger.pageX, finger.pageY);
31225         }
31226         
31227         var x = Math.pow(coords[0] - coords[2], 2);
31228         var y = Math.pow(coords[1] - coords[3], 2);
31229         
31230         this.startDistance = Math.sqrt(x + y);
31231         
31232         this.startScale = this.scale;
31233         
31234         this.pinching = true;
31235         this.dragable = false;
31236         
31237     },
31238     
31239     onTouchMove : function(e)
31240     {
31241         if(!this.pinching && !this.dragable){
31242             return;
31243         }
31244         
31245         var touches = e.browserEvent.touches;
31246         
31247         if(!touches){
31248             return;
31249         }
31250         
31251         if(this.dragable){
31252             this.onMouseMove(e);
31253             return;
31254         }
31255         
31256         var coords = [];
31257         
31258         for(var i = 0, finger; finger = touches[i]; i++){
31259             coords.push(finger.pageX, finger.pageY);
31260         }
31261         
31262         var x = Math.pow(coords[0] - coords[2], 2);
31263         var y = Math.pow(coords[1] - coords[3], 2);
31264         
31265         this.endDistance = Math.sqrt(x + y);
31266         
31267         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31268         
31269         if(!this.zoomable()){
31270             this.scale = this.startScale;
31271             return;
31272         }
31273         
31274         this.draw();
31275         
31276     },
31277     
31278     onTouchEnd : function(e)
31279     {
31280         this.pinching = false;
31281         this.dragable = false;
31282         
31283     },
31284     
31285     process : function(file, crop)
31286     {
31287         if(this.loadMask){
31288             this.maskEl.mask(this.loadingText);
31289         }
31290         
31291         this.xhr = new XMLHttpRequest();
31292         
31293         file.xhr = this.xhr;
31294
31295         this.xhr.open(this.method, this.url, true);
31296         
31297         var headers = {
31298             "Accept": "application/json",
31299             "Cache-Control": "no-cache",
31300             "X-Requested-With": "XMLHttpRequest"
31301         };
31302         
31303         for (var headerName in headers) {
31304             var headerValue = headers[headerName];
31305             if (headerValue) {
31306                 this.xhr.setRequestHeader(headerName, headerValue);
31307             }
31308         }
31309         
31310         var _this = this;
31311         
31312         this.xhr.onload = function()
31313         {
31314             _this.xhrOnLoad(_this.xhr);
31315         }
31316         
31317         this.xhr.onerror = function()
31318         {
31319             _this.xhrOnError(_this.xhr);
31320         }
31321         
31322         var formData = new FormData();
31323
31324         formData.append('returnHTML', 'NO');
31325         
31326         if(crop){
31327             formData.append('crop', crop);
31328         }
31329         
31330         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31331             formData.append(this.paramName, file, file.name);
31332         }
31333         
31334         if(typeof(file.filename) != 'undefined'){
31335             formData.append('filename', file.filename);
31336         }
31337         
31338         if(typeof(file.mimetype) != 'undefined'){
31339             formData.append('mimetype', file.mimetype);
31340         }
31341         
31342         if(this.fireEvent('arrange', this, formData) != false){
31343             this.xhr.send(formData);
31344         };
31345     },
31346     
31347     xhrOnLoad : function(xhr)
31348     {
31349         if(this.loadMask){
31350             this.maskEl.unmask();
31351         }
31352         
31353         if (xhr.readyState !== 4) {
31354             this.fireEvent('exception', this, xhr);
31355             return;
31356         }
31357
31358         var response = Roo.decode(xhr.responseText);
31359         
31360         if(!response.success){
31361             this.fireEvent('exception', this, xhr);
31362             return;
31363         }
31364         
31365         var response = Roo.decode(xhr.responseText);
31366         
31367         this.fireEvent('upload', this, response);
31368         
31369     },
31370     
31371     xhrOnError : function()
31372     {
31373         if(this.loadMask){
31374             this.maskEl.unmask();
31375         }
31376         
31377         Roo.log('xhr on error');
31378         
31379         var response = Roo.decode(xhr.responseText);
31380           
31381         Roo.log(response);
31382         
31383     },
31384     
31385     prepare : function(file)
31386     {   
31387         if(this.loadMask){
31388             this.maskEl.mask(this.loadingText);
31389         }
31390         
31391         this.file = false;
31392         this.exif = {};
31393         
31394         if(typeof(file) === 'string'){
31395             this.loadCanvas(file);
31396             return;
31397         }
31398         
31399         if(!file || !this.urlAPI){
31400             return;
31401         }
31402         
31403         this.file = file;
31404         this.cropType = file.type;
31405         
31406         var _this = this;
31407         
31408         if(this.fireEvent('prepare', this, this.file) != false){
31409             
31410             var reader = new FileReader();
31411             
31412             reader.onload = function (e) {
31413                 if (e.target.error) {
31414                     Roo.log(e.target.error);
31415                     return;
31416                 }
31417                 
31418                 var buffer = e.target.result,
31419                     dataView = new DataView(buffer),
31420                     offset = 2,
31421                     maxOffset = dataView.byteLength - 4,
31422                     markerBytes,
31423                     markerLength;
31424                 
31425                 if (dataView.getUint16(0) === 0xffd8) {
31426                     while (offset < maxOffset) {
31427                         markerBytes = dataView.getUint16(offset);
31428                         
31429                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31430                             markerLength = dataView.getUint16(offset + 2) + 2;
31431                             if (offset + markerLength > dataView.byteLength) {
31432                                 Roo.log('Invalid meta data: Invalid segment size.');
31433                                 break;
31434                             }
31435                             
31436                             if(markerBytes == 0xffe1){
31437                                 _this.parseExifData(
31438                                     dataView,
31439                                     offset,
31440                                     markerLength
31441                                 );
31442                             }
31443                             
31444                             offset += markerLength;
31445                             
31446                             continue;
31447                         }
31448                         
31449                         break;
31450                     }
31451                     
31452                 }
31453                 
31454                 var url = _this.urlAPI.createObjectURL(_this.file);
31455                 
31456                 _this.loadCanvas(url);
31457                 
31458                 return;
31459             }
31460             
31461             reader.readAsArrayBuffer(this.file);
31462             
31463         }
31464         
31465     },
31466     
31467     parseExifData : function(dataView, offset, length)
31468     {
31469         var tiffOffset = offset + 10,
31470             littleEndian,
31471             dirOffset;
31472     
31473         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31474             // No Exif data, might be XMP data instead
31475             return;
31476         }
31477         
31478         // Check for the ASCII code for "Exif" (0x45786966):
31479         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31480             // No Exif data, might be XMP data instead
31481             return;
31482         }
31483         if (tiffOffset + 8 > dataView.byteLength) {
31484             Roo.log('Invalid Exif data: Invalid segment size.');
31485             return;
31486         }
31487         // Check for the two null bytes:
31488         if (dataView.getUint16(offset + 8) !== 0x0000) {
31489             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31490             return;
31491         }
31492         // Check the byte alignment:
31493         switch (dataView.getUint16(tiffOffset)) {
31494         case 0x4949:
31495             littleEndian = true;
31496             break;
31497         case 0x4D4D:
31498             littleEndian = false;
31499             break;
31500         default:
31501             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31502             return;
31503         }
31504         // Check for the TIFF tag marker (0x002A):
31505         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31506             Roo.log('Invalid Exif data: Missing TIFF marker.');
31507             return;
31508         }
31509         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31510         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31511         
31512         this.parseExifTags(
31513             dataView,
31514             tiffOffset,
31515             tiffOffset + dirOffset,
31516             littleEndian
31517         );
31518     },
31519     
31520     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31521     {
31522         var tagsNumber,
31523             dirEndOffset,
31524             i;
31525         if (dirOffset + 6 > dataView.byteLength) {
31526             Roo.log('Invalid Exif data: Invalid directory offset.');
31527             return;
31528         }
31529         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31530         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31531         if (dirEndOffset + 4 > dataView.byteLength) {
31532             Roo.log('Invalid Exif data: Invalid directory size.');
31533             return;
31534         }
31535         for (i = 0; i < tagsNumber; i += 1) {
31536             this.parseExifTag(
31537                 dataView,
31538                 tiffOffset,
31539                 dirOffset + 2 + 12 * i, // tag offset
31540                 littleEndian
31541             );
31542         }
31543         // Return the offset to the next directory:
31544         return dataView.getUint32(dirEndOffset, littleEndian);
31545     },
31546     
31547     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31548     {
31549         var tag = dataView.getUint16(offset, littleEndian);
31550         
31551         this.exif[tag] = this.getExifValue(
31552             dataView,
31553             tiffOffset,
31554             offset,
31555             dataView.getUint16(offset + 2, littleEndian), // tag type
31556             dataView.getUint32(offset + 4, littleEndian), // tag length
31557             littleEndian
31558         );
31559     },
31560     
31561     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31562     {
31563         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31564             tagSize,
31565             dataOffset,
31566             values,
31567             i,
31568             str,
31569             c;
31570     
31571         if (!tagType) {
31572             Roo.log('Invalid Exif data: Invalid tag type.');
31573             return;
31574         }
31575         
31576         tagSize = tagType.size * length;
31577         // Determine if the value is contained in the dataOffset bytes,
31578         // or if the value at the dataOffset is a pointer to the actual data:
31579         dataOffset = tagSize > 4 ?
31580                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31581         if (dataOffset + tagSize > dataView.byteLength) {
31582             Roo.log('Invalid Exif data: Invalid data offset.');
31583             return;
31584         }
31585         if (length === 1) {
31586             return tagType.getValue(dataView, dataOffset, littleEndian);
31587         }
31588         values = [];
31589         for (i = 0; i < length; i += 1) {
31590             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31591         }
31592         
31593         if (tagType.ascii) {
31594             str = '';
31595             // Concatenate the chars:
31596             for (i = 0; i < values.length; i += 1) {
31597                 c = values[i];
31598                 // Ignore the terminating NULL byte(s):
31599                 if (c === '\u0000') {
31600                     break;
31601                 }
31602                 str += c;
31603             }
31604             return str;
31605         }
31606         return values;
31607     }
31608     
31609 });
31610
31611 Roo.apply(Roo.bootstrap.UploadCropbox, {
31612     tags : {
31613         'Orientation': 0x0112
31614     },
31615     
31616     Orientation: {
31617             1: 0, //'top-left',
31618 //            2: 'top-right',
31619             3: 180, //'bottom-right',
31620 //            4: 'bottom-left',
31621 //            5: 'left-top',
31622             6: 90, //'right-top',
31623 //            7: 'right-bottom',
31624             8: 270 //'left-bottom'
31625     },
31626     
31627     exifTagTypes : {
31628         // byte, 8-bit unsigned int:
31629         1: {
31630             getValue: function (dataView, dataOffset) {
31631                 return dataView.getUint8(dataOffset);
31632             },
31633             size: 1
31634         },
31635         // ascii, 8-bit byte:
31636         2: {
31637             getValue: function (dataView, dataOffset) {
31638                 return String.fromCharCode(dataView.getUint8(dataOffset));
31639             },
31640             size: 1,
31641             ascii: true
31642         },
31643         // short, 16 bit int:
31644         3: {
31645             getValue: function (dataView, dataOffset, littleEndian) {
31646                 return dataView.getUint16(dataOffset, littleEndian);
31647             },
31648             size: 2
31649         },
31650         // long, 32 bit int:
31651         4: {
31652             getValue: function (dataView, dataOffset, littleEndian) {
31653                 return dataView.getUint32(dataOffset, littleEndian);
31654             },
31655             size: 4
31656         },
31657         // rational = two long values, first is numerator, second is denominator:
31658         5: {
31659             getValue: function (dataView, dataOffset, littleEndian) {
31660                 return dataView.getUint32(dataOffset, littleEndian) /
31661                     dataView.getUint32(dataOffset + 4, littleEndian);
31662             },
31663             size: 8
31664         },
31665         // slong, 32 bit signed int:
31666         9: {
31667             getValue: function (dataView, dataOffset, littleEndian) {
31668                 return dataView.getInt32(dataOffset, littleEndian);
31669             },
31670             size: 4
31671         },
31672         // srational, two slongs, first is numerator, second is denominator:
31673         10: {
31674             getValue: function (dataView, dataOffset, littleEndian) {
31675                 return dataView.getInt32(dataOffset, littleEndian) /
31676                     dataView.getInt32(dataOffset + 4, littleEndian);
31677             },
31678             size: 8
31679         }
31680     },
31681     
31682     footer : {
31683         STANDARD : [
31684             {
31685                 tag : 'div',
31686                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31687                 action : 'rotate-left',
31688                 cn : [
31689                     {
31690                         tag : 'button',
31691                         cls : 'btn btn-default',
31692                         html : '<i class="fa fa-undo"></i>'
31693                     }
31694                 ]
31695             },
31696             {
31697                 tag : 'div',
31698                 cls : 'btn-group roo-upload-cropbox-picture',
31699                 action : 'picture',
31700                 cn : [
31701                     {
31702                         tag : 'button',
31703                         cls : 'btn btn-default',
31704                         html : '<i class="fa fa-picture-o"></i>'
31705                     }
31706                 ]
31707             },
31708             {
31709                 tag : 'div',
31710                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31711                 action : 'rotate-right',
31712                 cn : [
31713                     {
31714                         tag : 'button',
31715                         cls : 'btn btn-default',
31716                         html : '<i class="fa fa-repeat"></i>'
31717                     }
31718                 ]
31719             }
31720         ],
31721         DOCUMENT : [
31722             {
31723                 tag : 'div',
31724                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31725                 action : 'rotate-left',
31726                 cn : [
31727                     {
31728                         tag : 'button',
31729                         cls : 'btn btn-default',
31730                         html : '<i class="fa fa-undo"></i>'
31731                     }
31732                 ]
31733             },
31734             {
31735                 tag : 'div',
31736                 cls : 'btn-group roo-upload-cropbox-download',
31737                 action : 'download',
31738                 cn : [
31739                     {
31740                         tag : 'button',
31741                         cls : 'btn btn-default',
31742                         html : '<i class="fa fa-download"></i>'
31743                     }
31744                 ]
31745             },
31746             {
31747                 tag : 'div',
31748                 cls : 'btn-group roo-upload-cropbox-crop',
31749                 action : 'crop',
31750                 cn : [
31751                     {
31752                         tag : 'button',
31753                         cls : 'btn btn-default',
31754                         html : '<i class="fa fa-crop"></i>'
31755                     }
31756                 ]
31757             },
31758             {
31759                 tag : 'div',
31760                 cls : 'btn-group roo-upload-cropbox-trash',
31761                 action : 'trash',
31762                 cn : [
31763                     {
31764                         tag : 'button',
31765                         cls : 'btn btn-default',
31766                         html : '<i class="fa fa-trash"></i>'
31767                     }
31768                 ]
31769             },
31770             {
31771                 tag : 'div',
31772                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31773                 action : 'rotate-right',
31774                 cn : [
31775                     {
31776                         tag : 'button',
31777                         cls : 'btn btn-default',
31778                         html : '<i class="fa fa-repeat"></i>'
31779                     }
31780                 ]
31781             }
31782         ],
31783         ROTATOR : [
31784             {
31785                 tag : 'div',
31786                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31787                 action : 'rotate-left',
31788                 cn : [
31789                     {
31790                         tag : 'button',
31791                         cls : 'btn btn-default',
31792                         html : '<i class="fa fa-undo"></i>'
31793                     }
31794                 ]
31795             },
31796             {
31797                 tag : 'div',
31798                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31799                 action : 'rotate-right',
31800                 cn : [
31801                     {
31802                         tag : 'button',
31803                         cls : 'btn btn-default',
31804                         html : '<i class="fa fa-repeat"></i>'
31805                     }
31806                 ]
31807             }
31808         ]
31809     }
31810 });
31811
31812 /*
31813 * Licence: LGPL
31814 */
31815
31816 /**
31817  * @class Roo.bootstrap.DocumentManager
31818  * @extends Roo.bootstrap.Component
31819  * Bootstrap DocumentManager class
31820  * @cfg {String} paramName default 'imageUpload'
31821  * @cfg {String} toolTipName default 'filename'
31822  * @cfg {String} method default POST
31823  * @cfg {String} url action url
31824  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31825  * @cfg {Boolean} multiple multiple upload default true
31826  * @cfg {Number} thumbSize default 300
31827  * @cfg {String} fieldLabel
31828  * @cfg {Number} labelWidth default 4
31829  * @cfg {String} labelAlign (left|top) default left
31830  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31831 * @cfg {Number} labellg set the width of label (1-12)
31832  * @cfg {Number} labelmd set the width of label (1-12)
31833  * @cfg {Number} labelsm set the width of label (1-12)
31834  * @cfg {Number} labelxs set the width of label (1-12)
31835  * 
31836  * @constructor
31837  * Create a new DocumentManager
31838  * @param {Object} config The config object
31839  */
31840
31841 Roo.bootstrap.DocumentManager = function(config){
31842     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31843     
31844     this.files = [];
31845     this.delegates = [];
31846     
31847     this.addEvents({
31848         /**
31849          * @event initial
31850          * Fire when initial the DocumentManager
31851          * @param {Roo.bootstrap.DocumentManager} this
31852          */
31853         "initial" : true,
31854         /**
31855          * @event inspect
31856          * inspect selected file
31857          * @param {Roo.bootstrap.DocumentManager} this
31858          * @param {File} file
31859          */
31860         "inspect" : true,
31861         /**
31862          * @event exception
31863          * Fire when xhr load exception
31864          * @param {Roo.bootstrap.DocumentManager} this
31865          * @param {XMLHttpRequest} xhr
31866          */
31867         "exception" : true,
31868         /**
31869          * @event afterupload
31870          * Fire when xhr load exception
31871          * @param {Roo.bootstrap.DocumentManager} this
31872          * @param {XMLHttpRequest} xhr
31873          */
31874         "afterupload" : true,
31875         /**
31876          * @event prepare
31877          * prepare the form data
31878          * @param {Roo.bootstrap.DocumentManager} this
31879          * @param {Object} formData
31880          */
31881         "prepare" : true,
31882         /**
31883          * @event remove
31884          * Fire when remove the file
31885          * @param {Roo.bootstrap.DocumentManager} this
31886          * @param {Object} file
31887          */
31888         "remove" : true,
31889         /**
31890          * @event refresh
31891          * Fire after refresh the file
31892          * @param {Roo.bootstrap.DocumentManager} this
31893          */
31894         "refresh" : true,
31895         /**
31896          * @event click
31897          * Fire after click the image
31898          * @param {Roo.bootstrap.DocumentManager} this
31899          * @param {Object} file
31900          */
31901         "click" : true,
31902         /**
31903          * @event edit
31904          * Fire when upload a image and editable set to true
31905          * @param {Roo.bootstrap.DocumentManager} this
31906          * @param {Object} file
31907          */
31908         "edit" : true,
31909         /**
31910          * @event beforeselectfile
31911          * Fire before select file
31912          * @param {Roo.bootstrap.DocumentManager} this
31913          */
31914         "beforeselectfile" : true,
31915         /**
31916          * @event process
31917          * Fire before process file
31918          * @param {Roo.bootstrap.DocumentManager} this
31919          * @param {Object} file
31920          */
31921         "process" : true,
31922         /**
31923          * @event previewrendered
31924          * Fire when preview rendered
31925          * @param {Roo.bootstrap.DocumentManager} this
31926          * @param {Object} file
31927          */
31928         "previewrendered" : true,
31929         /**
31930          */
31931         "previewResize" : true
31932         
31933     });
31934 };
31935
31936 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31937     
31938     boxes : 0,
31939     inputName : '',
31940     thumbSize : 300,
31941     multiple : true,
31942     files : false,
31943     method : 'POST',
31944     url : '',
31945     paramName : 'imageUpload',
31946     toolTipName : 'filename',
31947     fieldLabel : '',
31948     labelWidth : 4,
31949     labelAlign : 'left',
31950     editable : true,
31951     delegates : false,
31952     xhr : false, 
31953     
31954     labellg : 0,
31955     labelmd : 0,
31956     labelsm : 0,
31957     labelxs : 0,
31958     
31959     getAutoCreate : function()
31960     {   
31961         var managerWidget = {
31962             tag : 'div',
31963             cls : 'roo-document-manager',
31964             cn : [
31965                 {
31966                     tag : 'input',
31967                     cls : 'roo-document-manager-selector',
31968                     type : 'file'
31969                 },
31970                 {
31971                     tag : 'div',
31972                     cls : 'roo-document-manager-uploader',
31973                     cn : [
31974                         {
31975                             tag : 'div',
31976                             cls : 'roo-document-manager-upload-btn',
31977                             html : '<i class="fa fa-plus"></i>'
31978                         }
31979                     ]
31980                     
31981                 }
31982             ]
31983         };
31984         
31985         var content = [
31986             {
31987                 tag : 'div',
31988                 cls : 'column col-md-12',
31989                 cn : managerWidget
31990             }
31991         ];
31992         
31993         if(this.fieldLabel.length){
31994             
31995             content = [
31996                 {
31997                     tag : 'div',
31998                     cls : 'column col-md-12',
31999                     html : this.fieldLabel
32000                 },
32001                 {
32002                     tag : 'div',
32003                     cls : 'column col-md-12',
32004                     cn : managerWidget
32005                 }
32006             ];
32007
32008             if(this.labelAlign == 'left'){
32009                 content = [
32010                     {
32011                         tag : 'div',
32012                         cls : 'column',
32013                         html : this.fieldLabel
32014                     },
32015                     {
32016                         tag : 'div',
32017                         cls : 'column',
32018                         cn : managerWidget
32019                     }
32020                 ];
32021                 
32022                 if(this.labelWidth > 12){
32023                     content[0].style = "width: " + this.labelWidth + 'px';
32024                 }
32025
32026                 if(this.labelWidth < 13 && this.labelmd == 0){
32027                     this.labelmd = this.labelWidth;
32028                 }
32029
32030                 if(this.labellg > 0){
32031                     content[0].cls += ' col-lg-' + this.labellg;
32032                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32033                 }
32034
32035                 if(this.labelmd > 0){
32036                     content[0].cls += ' col-md-' + this.labelmd;
32037                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32038                 }
32039
32040                 if(this.labelsm > 0){
32041                     content[0].cls += ' col-sm-' + this.labelsm;
32042                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32043                 }
32044
32045                 if(this.labelxs > 0){
32046                     content[0].cls += ' col-xs-' + this.labelxs;
32047                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32048                 }
32049                 
32050             }
32051         }
32052         
32053         var cfg = {
32054             tag : 'div',
32055             cls : 'row clearfix',
32056             cn : content
32057         };
32058         
32059         return cfg;
32060         
32061     },
32062     
32063     initEvents : function()
32064     {
32065         this.managerEl = this.el.select('.roo-document-manager', true).first();
32066         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32067         
32068         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32069         this.selectorEl.hide();
32070         
32071         if(this.multiple){
32072             this.selectorEl.attr('multiple', 'multiple');
32073         }
32074         
32075         this.selectorEl.on('change', this.onFileSelected, this);
32076         
32077         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32078         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32079         
32080         this.uploader.on('click', this.onUploaderClick, this);
32081         
32082         this.renderProgressDialog();
32083         
32084         var _this = this;
32085         
32086         window.addEventListener("resize", function() { _this.refresh(); } );
32087         
32088         this.fireEvent('initial', this);
32089     },
32090     
32091     renderProgressDialog : function()
32092     {
32093         var _this = this;
32094         
32095         this.progressDialog = new Roo.bootstrap.Modal({
32096             cls : 'roo-document-manager-progress-dialog',
32097             allow_close : false,
32098             animate : false,
32099             title : '',
32100             buttons : [
32101                 {
32102                     name  :'cancel',
32103                     weight : 'danger',
32104                     html : 'Cancel'
32105                 }
32106             ], 
32107             listeners : { 
32108                 btnclick : function() {
32109                     _this.uploadCancel();
32110                     this.hide();
32111                 }
32112             }
32113         });
32114          
32115         this.progressDialog.render(Roo.get(document.body));
32116          
32117         this.progress = new Roo.bootstrap.Progress({
32118             cls : 'roo-document-manager-progress',
32119             active : true,
32120             striped : true
32121         });
32122         
32123         this.progress.render(this.progressDialog.getChildContainer());
32124         
32125         this.progressBar = new Roo.bootstrap.ProgressBar({
32126             cls : 'roo-document-manager-progress-bar',
32127             aria_valuenow : 0,
32128             aria_valuemin : 0,
32129             aria_valuemax : 12,
32130             panel : 'success'
32131         });
32132         
32133         this.progressBar.render(this.progress.getChildContainer());
32134     },
32135     
32136     onUploaderClick : function(e)
32137     {
32138         e.preventDefault();
32139      
32140         if(this.fireEvent('beforeselectfile', this) != false){
32141             this.selectorEl.dom.click();
32142         }
32143         
32144     },
32145     
32146     onFileSelected : function(e)
32147     {
32148         e.preventDefault();
32149         
32150         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32151             return;
32152         }
32153         
32154         Roo.each(this.selectorEl.dom.files, function(file){
32155             if(this.fireEvent('inspect', this, file) != false){
32156                 this.files.push(file);
32157             }
32158         }, this);
32159         
32160         this.queue();
32161         
32162     },
32163     
32164     queue : function()
32165     {
32166         this.selectorEl.dom.value = '';
32167         
32168         if(!this.files || !this.files.length){
32169             return;
32170         }
32171         
32172         if(this.boxes > 0 && this.files.length > this.boxes){
32173             this.files = this.files.slice(0, this.boxes);
32174         }
32175         
32176         this.uploader.show();
32177         
32178         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32179             this.uploader.hide();
32180         }
32181         
32182         var _this = this;
32183         
32184         var files = [];
32185         
32186         var docs = [];
32187         
32188         Roo.each(this.files, function(file){
32189             
32190             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32191                 var f = this.renderPreview(file);
32192                 files.push(f);
32193                 return;
32194             }
32195             
32196             if(file.type.indexOf('image') != -1){
32197                 this.delegates.push(
32198                     (function(){
32199                         _this.process(file);
32200                     }).createDelegate(this)
32201                 );
32202         
32203                 return;
32204             }
32205             
32206             docs.push(
32207                 (function(){
32208                     _this.process(file);
32209                 }).createDelegate(this)
32210             );
32211             
32212         }, this);
32213         
32214         this.files = files;
32215         
32216         this.delegates = this.delegates.concat(docs);
32217         
32218         if(!this.delegates.length){
32219             this.refresh();
32220             return;
32221         }
32222         
32223         this.progressBar.aria_valuemax = this.delegates.length;
32224         
32225         this.arrange();
32226         
32227         return;
32228     },
32229     
32230     arrange : function()
32231     {
32232         if(!this.delegates.length){
32233             this.progressDialog.hide();
32234             this.refresh();
32235             return;
32236         }
32237         
32238         var delegate = this.delegates.shift();
32239         
32240         this.progressDialog.show();
32241         
32242         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32243         
32244         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32245         
32246         delegate();
32247     },
32248     
32249     refresh : function()
32250     {
32251         this.uploader.show();
32252         
32253         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32254             this.uploader.hide();
32255         }
32256         
32257         Roo.isTouch ? this.closable(false) : this.closable(true);
32258         
32259         this.fireEvent('refresh', this);
32260     },
32261     
32262     onRemove : function(e, el, o)
32263     {
32264         e.preventDefault();
32265         
32266         this.fireEvent('remove', this, o);
32267         
32268     },
32269     
32270     remove : function(o)
32271     {
32272         var files = [];
32273         
32274         Roo.each(this.files, function(file){
32275             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32276                 files.push(file);
32277                 return;
32278             }
32279
32280             o.target.remove();
32281
32282         }, this);
32283         
32284         this.files = files;
32285         
32286         this.refresh();
32287     },
32288     
32289     clear : function()
32290     {
32291         Roo.each(this.files, function(file){
32292             if(!file.target){
32293                 return;
32294             }
32295             
32296             file.target.remove();
32297
32298         }, this);
32299         
32300         this.files = [];
32301         
32302         this.refresh();
32303     },
32304     
32305     onClick : function(e, el, o)
32306     {
32307         e.preventDefault();
32308         
32309         this.fireEvent('click', this, o);
32310         
32311     },
32312     
32313     closable : function(closable)
32314     {
32315         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32316             
32317             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32318             
32319             if(closable){
32320                 el.show();
32321                 return;
32322             }
32323             
32324             el.hide();
32325             
32326         }, this);
32327     },
32328     
32329     xhrOnLoad : function(xhr)
32330     {
32331         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32332             el.remove();
32333         }, this);
32334         
32335         if (xhr.readyState !== 4) {
32336             this.arrange();
32337             this.fireEvent('exception', this, xhr);
32338             return;
32339         }
32340
32341         var response = Roo.decode(xhr.responseText);
32342         
32343         if(!response.success){
32344             this.arrange();
32345             this.fireEvent('exception', this, xhr);
32346             return;
32347         }
32348         
32349         var file = this.renderPreview(response.data);
32350         
32351         this.files.push(file);
32352         
32353         this.arrange();
32354         
32355         this.fireEvent('afterupload', this, xhr);
32356         
32357     },
32358     
32359     xhrOnError : function(xhr)
32360     {
32361         Roo.log('xhr on error');
32362         
32363         var response = Roo.decode(xhr.responseText);
32364           
32365         Roo.log(response);
32366         
32367         this.arrange();
32368     },
32369     
32370     process : function(file)
32371     {
32372         if(this.fireEvent('process', this, file) !== false){
32373             if(this.editable && file.type.indexOf('image') != -1){
32374                 this.fireEvent('edit', this, file);
32375                 return;
32376             }
32377
32378             this.uploadStart(file, false);
32379
32380             return;
32381         }
32382         
32383     },
32384     
32385     uploadStart : function(file, crop)
32386     {
32387         this.xhr = new XMLHttpRequest();
32388         
32389         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32390             this.arrange();
32391             return;
32392         }
32393         
32394         file.xhr = this.xhr;
32395             
32396         this.managerEl.createChild({
32397             tag : 'div',
32398             cls : 'roo-document-manager-loading',
32399             cn : [
32400                 {
32401                     tag : 'div',
32402                     tooltip : file.name,
32403                     cls : 'roo-document-manager-thumb',
32404                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32405                 }
32406             ]
32407
32408         });
32409
32410         this.xhr.open(this.method, this.url, true);
32411         
32412         var headers = {
32413             "Accept": "application/json",
32414             "Cache-Control": "no-cache",
32415             "X-Requested-With": "XMLHttpRequest"
32416         };
32417         
32418         for (var headerName in headers) {
32419             var headerValue = headers[headerName];
32420             if (headerValue) {
32421                 this.xhr.setRequestHeader(headerName, headerValue);
32422             }
32423         }
32424         
32425         var _this = this;
32426         
32427         this.xhr.onload = function()
32428         {
32429             _this.xhrOnLoad(_this.xhr);
32430         }
32431         
32432         this.xhr.onerror = function()
32433         {
32434             _this.xhrOnError(_this.xhr);
32435         }
32436         
32437         var formData = new FormData();
32438
32439         formData.append('returnHTML', 'NO');
32440         
32441         if(crop){
32442             formData.append('crop', crop);
32443         }
32444         
32445         formData.append(this.paramName, file, file.name);
32446         
32447         var options = {
32448             file : file, 
32449             manually : false
32450         };
32451         
32452         if(this.fireEvent('prepare', this, formData, options) != false){
32453             
32454             if(options.manually){
32455                 return;
32456             }
32457             
32458             this.xhr.send(formData);
32459             return;
32460         };
32461         
32462         this.uploadCancel();
32463     },
32464     
32465     uploadCancel : function()
32466     {
32467         if (this.xhr) {
32468             this.xhr.abort();
32469         }
32470         
32471         this.delegates = [];
32472         
32473         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32474             el.remove();
32475         }, this);
32476         
32477         this.arrange();
32478     },
32479     
32480     renderPreview : function(file)
32481     {
32482         if(typeof(file.target) != 'undefined' && file.target){
32483             return file;
32484         }
32485         
32486         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32487         
32488         var previewEl = this.managerEl.createChild({
32489             tag : 'div',
32490             cls : 'roo-document-manager-preview',
32491             cn : [
32492                 {
32493                     tag : 'div',
32494                     tooltip : file[this.toolTipName],
32495                     cls : 'roo-document-manager-thumb',
32496                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32497                 },
32498                 {
32499                     tag : 'button',
32500                     cls : 'close',
32501                     html : '<i class="fa fa-times-circle"></i>'
32502                 }
32503             ]
32504         });
32505
32506         var close = previewEl.select('button.close', true).first();
32507
32508         close.on('click', this.onRemove, this, file);
32509
32510         file.target = previewEl;
32511
32512         var image = previewEl.select('img', true).first();
32513         
32514         var _this = this;
32515         
32516         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32517         
32518         image.on('click', this.onClick, this, file);
32519         
32520         this.fireEvent('previewrendered', this, file);
32521         
32522         return file;
32523         
32524     },
32525     
32526     onPreviewLoad : function(file, image)
32527     {
32528         if(typeof(file.target) == 'undefined' || !file.target){
32529             return;
32530         }
32531         
32532         var width = image.dom.naturalWidth || image.dom.width;
32533         var height = image.dom.naturalHeight || image.dom.height;
32534         
32535         if(!this.previewResize) {
32536             return;
32537         }
32538         
32539         if(width > height){
32540             file.target.addClass('wide');
32541             return;
32542         }
32543         
32544         file.target.addClass('tall');
32545         return;
32546         
32547     },
32548     
32549     uploadFromSource : function(file, crop)
32550     {
32551         this.xhr = new XMLHttpRequest();
32552         
32553         this.managerEl.createChild({
32554             tag : 'div',
32555             cls : 'roo-document-manager-loading',
32556             cn : [
32557                 {
32558                     tag : 'div',
32559                     tooltip : file.name,
32560                     cls : 'roo-document-manager-thumb',
32561                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32562                 }
32563             ]
32564
32565         });
32566
32567         this.xhr.open(this.method, this.url, true);
32568         
32569         var headers = {
32570             "Accept": "application/json",
32571             "Cache-Control": "no-cache",
32572             "X-Requested-With": "XMLHttpRequest"
32573         };
32574         
32575         for (var headerName in headers) {
32576             var headerValue = headers[headerName];
32577             if (headerValue) {
32578                 this.xhr.setRequestHeader(headerName, headerValue);
32579             }
32580         }
32581         
32582         var _this = this;
32583         
32584         this.xhr.onload = function()
32585         {
32586             _this.xhrOnLoad(_this.xhr);
32587         }
32588         
32589         this.xhr.onerror = function()
32590         {
32591             _this.xhrOnError(_this.xhr);
32592         }
32593         
32594         var formData = new FormData();
32595
32596         formData.append('returnHTML', 'NO');
32597         
32598         formData.append('crop', crop);
32599         
32600         if(typeof(file.filename) != 'undefined'){
32601             formData.append('filename', file.filename);
32602         }
32603         
32604         if(typeof(file.mimetype) != 'undefined'){
32605             formData.append('mimetype', file.mimetype);
32606         }
32607         
32608         Roo.log(formData);
32609         
32610         if(this.fireEvent('prepare', this, formData) != false){
32611             this.xhr.send(formData);
32612         };
32613     }
32614 });
32615
32616 /*
32617 * Licence: LGPL
32618 */
32619
32620 /**
32621  * @class Roo.bootstrap.DocumentViewer
32622  * @extends Roo.bootstrap.Component
32623  * Bootstrap DocumentViewer class
32624  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32625  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32626  * 
32627  * @constructor
32628  * Create a new DocumentViewer
32629  * @param {Object} config The config object
32630  */
32631
32632 Roo.bootstrap.DocumentViewer = function(config){
32633     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32634     
32635     this.addEvents({
32636         /**
32637          * @event initial
32638          * Fire after initEvent
32639          * @param {Roo.bootstrap.DocumentViewer} this
32640          */
32641         "initial" : true,
32642         /**
32643          * @event click
32644          * Fire after click
32645          * @param {Roo.bootstrap.DocumentViewer} this
32646          */
32647         "click" : true,
32648         /**
32649          * @event download
32650          * Fire after download button
32651          * @param {Roo.bootstrap.DocumentViewer} this
32652          */
32653         "download" : true,
32654         /**
32655          * @event trash
32656          * Fire after trash button
32657          * @param {Roo.bootstrap.DocumentViewer} this
32658          */
32659         "trash" : true
32660         
32661     });
32662 };
32663
32664 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32665     
32666     showDownload : true,
32667     
32668     showTrash : true,
32669     
32670     getAutoCreate : function()
32671     {
32672         var cfg = {
32673             tag : 'div',
32674             cls : 'roo-document-viewer',
32675             cn : [
32676                 {
32677                     tag : 'div',
32678                     cls : 'roo-document-viewer-body',
32679                     cn : [
32680                         {
32681                             tag : 'div',
32682                             cls : 'roo-document-viewer-thumb',
32683                             cn : [
32684                                 {
32685                                     tag : 'img',
32686                                     cls : 'roo-document-viewer-image'
32687                                 }
32688                             ]
32689                         }
32690                     ]
32691                 },
32692                 {
32693                     tag : 'div',
32694                     cls : 'roo-document-viewer-footer',
32695                     cn : {
32696                         tag : 'div',
32697                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32698                         cn : [
32699                             {
32700                                 tag : 'div',
32701                                 cls : 'btn-group roo-document-viewer-download',
32702                                 cn : [
32703                                     {
32704                                         tag : 'button',
32705                                         cls : 'btn btn-default',
32706                                         html : '<i class="fa fa-download"></i>'
32707                                     }
32708                                 ]
32709                             },
32710                             {
32711                                 tag : 'div',
32712                                 cls : 'btn-group roo-document-viewer-trash',
32713                                 cn : [
32714                                     {
32715                                         tag : 'button',
32716                                         cls : 'btn btn-default',
32717                                         html : '<i class="fa fa-trash"></i>'
32718                                     }
32719                                 ]
32720                             }
32721                         ]
32722                     }
32723                 }
32724             ]
32725         };
32726         
32727         return cfg;
32728     },
32729     
32730     initEvents : function()
32731     {
32732         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32733         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32734         
32735         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32736         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32737         
32738         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32739         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32740         
32741         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32742         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32743         
32744         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32745         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32746         
32747         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32748         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32749         
32750         this.bodyEl.on('click', this.onClick, this);
32751         this.downloadBtn.on('click', this.onDownload, this);
32752         this.trashBtn.on('click', this.onTrash, this);
32753         
32754         this.downloadBtn.hide();
32755         this.trashBtn.hide();
32756         
32757         if(this.showDownload){
32758             this.downloadBtn.show();
32759         }
32760         
32761         if(this.showTrash){
32762             this.trashBtn.show();
32763         }
32764         
32765         if(!this.showDownload && !this.showTrash) {
32766             this.footerEl.hide();
32767         }
32768         
32769     },
32770     
32771     initial : function()
32772     {
32773         this.fireEvent('initial', this);
32774         
32775     },
32776     
32777     onClick : function(e)
32778     {
32779         e.preventDefault();
32780         
32781         this.fireEvent('click', this);
32782     },
32783     
32784     onDownload : function(e)
32785     {
32786         e.preventDefault();
32787         
32788         this.fireEvent('download', this);
32789     },
32790     
32791     onTrash : function(e)
32792     {
32793         e.preventDefault();
32794         
32795         this.fireEvent('trash', this);
32796     }
32797     
32798 });
32799 /*
32800  * - LGPL
32801  *
32802  * nav progress bar
32803  * 
32804  */
32805
32806 /**
32807  * @class Roo.bootstrap.NavProgressBar
32808  * @extends Roo.bootstrap.Component
32809  * Bootstrap NavProgressBar class
32810  * 
32811  * @constructor
32812  * Create a new nav progress bar
32813  * @param {Object} config The config object
32814  */
32815
32816 Roo.bootstrap.NavProgressBar = function(config){
32817     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32818
32819     this.bullets = this.bullets || [];
32820    
32821 //    Roo.bootstrap.NavProgressBar.register(this);
32822      this.addEvents({
32823         /**
32824              * @event changed
32825              * Fires when the active item changes
32826              * @param {Roo.bootstrap.NavProgressBar} this
32827              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32828              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32829          */
32830         'changed': true
32831      });
32832     
32833 };
32834
32835 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32836     
32837     bullets : [],
32838     barItems : [],
32839     
32840     getAutoCreate : function()
32841     {
32842         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32843         
32844         cfg = {
32845             tag : 'div',
32846             cls : 'roo-navigation-bar-group',
32847             cn : [
32848                 {
32849                     tag : 'div',
32850                     cls : 'roo-navigation-top-bar'
32851                 },
32852                 {
32853                     tag : 'div',
32854                     cls : 'roo-navigation-bullets-bar',
32855                     cn : [
32856                         {
32857                             tag : 'ul',
32858                             cls : 'roo-navigation-bar'
32859                         }
32860                     ]
32861                 },
32862                 
32863                 {
32864                     tag : 'div',
32865                     cls : 'roo-navigation-bottom-bar'
32866                 }
32867             ]
32868             
32869         };
32870         
32871         return cfg;
32872         
32873     },
32874     
32875     initEvents: function() 
32876     {
32877         
32878     },
32879     
32880     onRender : function(ct, position) 
32881     {
32882         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32883         
32884         if(this.bullets.length){
32885             Roo.each(this.bullets, function(b){
32886                this.addItem(b);
32887             }, this);
32888         }
32889         
32890         this.format();
32891         
32892     },
32893     
32894     addItem : function(cfg)
32895     {
32896         var item = new Roo.bootstrap.NavProgressItem(cfg);
32897         
32898         item.parentId = this.id;
32899         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32900         
32901         if(cfg.html){
32902             var top = new Roo.bootstrap.Element({
32903                 tag : 'div',
32904                 cls : 'roo-navigation-bar-text'
32905             });
32906             
32907             var bottom = new Roo.bootstrap.Element({
32908                 tag : 'div',
32909                 cls : 'roo-navigation-bar-text'
32910             });
32911             
32912             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32913             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32914             
32915             var topText = new Roo.bootstrap.Element({
32916                 tag : 'span',
32917                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32918             });
32919             
32920             var bottomText = new Roo.bootstrap.Element({
32921                 tag : 'span',
32922                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32923             });
32924             
32925             topText.onRender(top.el, null);
32926             bottomText.onRender(bottom.el, null);
32927             
32928             item.topEl = top;
32929             item.bottomEl = bottom;
32930         }
32931         
32932         this.barItems.push(item);
32933         
32934         return item;
32935     },
32936     
32937     getActive : function()
32938     {
32939         var active = false;
32940         
32941         Roo.each(this.barItems, function(v){
32942             
32943             if (!v.isActive()) {
32944                 return;
32945             }
32946             
32947             active = v;
32948             return false;
32949             
32950         });
32951         
32952         return active;
32953     },
32954     
32955     setActiveItem : function(item)
32956     {
32957         var prev = false;
32958         
32959         Roo.each(this.barItems, function(v){
32960             if (v.rid == item.rid) {
32961                 return ;
32962             }
32963             
32964             if (v.isActive()) {
32965                 v.setActive(false);
32966                 prev = v;
32967             }
32968         });
32969
32970         item.setActive(true);
32971         
32972         this.fireEvent('changed', this, item, prev);
32973     },
32974     
32975     getBarItem: function(rid)
32976     {
32977         var ret = false;
32978         
32979         Roo.each(this.barItems, function(e) {
32980             if (e.rid != rid) {
32981                 return;
32982             }
32983             
32984             ret =  e;
32985             return false;
32986         });
32987         
32988         return ret;
32989     },
32990     
32991     indexOfItem : function(item)
32992     {
32993         var index = false;
32994         
32995         Roo.each(this.barItems, function(v, i){
32996             
32997             if (v.rid != item.rid) {
32998                 return;
32999             }
33000             
33001             index = i;
33002             return false
33003         });
33004         
33005         return index;
33006     },
33007     
33008     setActiveNext : function()
33009     {
33010         var i = this.indexOfItem(this.getActive());
33011         
33012         if (i > this.barItems.length) {
33013             return;
33014         }
33015         
33016         this.setActiveItem(this.barItems[i+1]);
33017     },
33018     
33019     setActivePrev : function()
33020     {
33021         var i = this.indexOfItem(this.getActive());
33022         
33023         if (i  < 1) {
33024             return;
33025         }
33026         
33027         this.setActiveItem(this.barItems[i-1]);
33028     },
33029     
33030     format : function()
33031     {
33032         if(!this.barItems.length){
33033             return;
33034         }
33035      
33036         var width = 100 / this.barItems.length;
33037         
33038         Roo.each(this.barItems, function(i){
33039             i.el.setStyle('width', width + '%');
33040             i.topEl.el.setStyle('width', width + '%');
33041             i.bottomEl.el.setStyle('width', width + '%');
33042         }, this);
33043         
33044     }
33045     
33046 });
33047 /*
33048  * - LGPL
33049  *
33050  * Nav Progress Item
33051  * 
33052  */
33053
33054 /**
33055  * @class Roo.bootstrap.NavProgressItem
33056  * @extends Roo.bootstrap.Component
33057  * Bootstrap NavProgressItem class
33058  * @cfg {String} rid the reference id
33059  * @cfg {Boolean} active (true|false) Is item active default false
33060  * @cfg {Boolean} disabled (true|false) Is item active default false
33061  * @cfg {String} html
33062  * @cfg {String} position (top|bottom) text position default bottom
33063  * @cfg {String} icon show icon instead of number
33064  * 
33065  * @constructor
33066  * Create a new NavProgressItem
33067  * @param {Object} config The config object
33068  */
33069 Roo.bootstrap.NavProgressItem = function(config){
33070     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33071     this.addEvents({
33072         // raw events
33073         /**
33074          * @event click
33075          * The raw click event for the entire grid.
33076          * @param {Roo.bootstrap.NavProgressItem} this
33077          * @param {Roo.EventObject} e
33078          */
33079         "click" : true
33080     });
33081    
33082 };
33083
33084 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33085     
33086     rid : '',
33087     active : false,
33088     disabled : false,
33089     html : '',
33090     position : 'bottom',
33091     icon : false,
33092     
33093     getAutoCreate : function()
33094     {
33095         var iconCls = 'roo-navigation-bar-item-icon';
33096         
33097         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33098         
33099         var cfg = {
33100             tag: 'li',
33101             cls: 'roo-navigation-bar-item',
33102             cn : [
33103                 {
33104                     tag : 'i',
33105                     cls : iconCls
33106                 }
33107             ]
33108         };
33109         
33110         if(this.active){
33111             cfg.cls += ' active';
33112         }
33113         if(this.disabled){
33114             cfg.cls += ' disabled';
33115         }
33116         
33117         return cfg;
33118     },
33119     
33120     disable : function()
33121     {
33122         this.setDisabled(true);
33123     },
33124     
33125     enable : function()
33126     {
33127         this.setDisabled(false);
33128     },
33129     
33130     initEvents: function() 
33131     {
33132         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33133         
33134         this.iconEl.on('click', this.onClick, this);
33135     },
33136     
33137     onClick : function(e)
33138     {
33139         e.preventDefault();
33140         
33141         if(this.disabled){
33142             return;
33143         }
33144         
33145         if(this.fireEvent('click', this, e) === false){
33146             return;
33147         };
33148         
33149         this.parent().setActiveItem(this);
33150     },
33151     
33152     isActive: function () 
33153     {
33154         return this.active;
33155     },
33156     
33157     setActive : function(state)
33158     {
33159         if(this.active == state){
33160             return;
33161         }
33162         
33163         this.active = state;
33164         
33165         if (state) {
33166             this.el.addClass('active');
33167             return;
33168         }
33169         
33170         this.el.removeClass('active');
33171         
33172         return;
33173     },
33174     
33175     setDisabled : function(state)
33176     {
33177         if(this.disabled == state){
33178             return;
33179         }
33180         
33181         this.disabled = state;
33182         
33183         if (state) {
33184             this.el.addClass('disabled');
33185             return;
33186         }
33187         
33188         this.el.removeClass('disabled');
33189     },
33190     
33191     tooltipEl : function()
33192     {
33193         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33194     }
33195 });
33196  
33197
33198  /*
33199  * - LGPL
33200  *
33201  * FieldLabel
33202  * 
33203  */
33204
33205 /**
33206  * @class Roo.bootstrap.FieldLabel
33207  * @extends Roo.bootstrap.Component
33208  * Bootstrap FieldLabel class
33209  * @cfg {String} html contents of the element
33210  * @cfg {String} tag tag of the element default label
33211  * @cfg {String} cls class of the element
33212  * @cfg {String} target label target 
33213  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33214  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33215  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33216  * @cfg {String} iconTooltip default "This field is required"
33217  * @cfg {String} indicatorpos (left|right) default left
33218  * 
33219  * @constructor
33220  * Create a new FieldLabel
33221  * @param {Object} config The config object
33222  */
33223
33224 Roo.bootstrap.FieldLabel = function(config){
33225     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33226     
33227     this.addEvents({
33228             /**
33229              * @event invalid
33230              * Fires after the field has been marked as invalid.
33231              * @param {Roo.form.FieldLabel} this
33232              * @param {String} msg The validation message
33233              */
33234             invalid : true,
33235             /**
33236              * @event valid
33237              * Fires after the field has been validated with no errors.
33238              * @param {Roo.form.FieldLabel} this
33239              */
33240             valid : true
33241         });
33242 };
33243
33244 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33245     
33246     tag: 'label',
33247     cls: '',
33248     html: '',
33249     target: '',
33250     allowBlank : true,
33251     invalidClass : 'has-warning',
33252     validClass : 'has-success',
33253     iconTooltip : 'This field is required',
33254     indicatorpos : 'left',
33255     
33256     getAutoCreate : function(){
33257         
33258         var cls = "";
33259         if (!this.allowBlank) {
33260             cls  = "visible";
33261         }
33262         
33263         var cfg = {
33264             tag : this.tag,
33265             cls : 'roo-bootstrap-field-label ' + this.cls,
33266             for : this.target,
33267             cn : [
33268                 {
33269                     tag : 'i',
33270                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33271                     tooltip : this.iconTooltip
33272                 },
33273                 {
33274                     tag : 'span',
33275                     html : this.html
33276                 }
33277             ] 
33278         };
33279         
33280         if(this.indicatorpos == 'right'){
33281             var cfg = {
33282                 tag : this.tag,
33283                 cls : 'roo-bootstrap-field-label ' + this.cls,
33284                 for : this.target,
33285                 cn : [
33286                     {
33287                         tag : 'span',
33288                         html : this.html
33289                     },
33290                     {
33291                         tag : 'i',
33292                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33293                         tooltip : this.iconTooltip
33294                     }
33295                 ] 
33296             };
33297         }
33298         
33299         return cfg;
33300     },
33301     
33302     initEvents: function() 
33303     {
33304         Roo.bootstrap.Element.superclass.initEvents.call(this);
33305         
33306         this.indicator = this.indicatorEl();
33307         
33308         if(this.indicator){
33309             this.indicator.removeClass('visible');
33310             this.indicator.addClass('invisible');
33311         }
33312         
33313         Roo.bootstrap.FieldLabel.register(this);
33314     },
33315     
33316     indicatorEl : function()
33317     {
33318         var indicator = this.el.select('i.roo-required-indicator',true).first();
33319         
33320         if(!indicator){
33321             return false;
33322         }
33323         
33324         return indicator;
33325         
33326     },
33327     
33328     /**
33329      * Mark this field as valid
33330      */
33331     markValid : function()
33332     {
33333         if(this.indicator){
33334             this.indicator.removeClass('visible');
33335             this.indicator.addClass('invisible');
33336         }
33337         if (Roo.bootstrap.version == 3) {
33338             this.el.removeClass(this.invalidClass);
33339             this.el.addClass(this.validClass);
33340         } else {
33341             this.el.removeClass('is-invalid');
33342             this.el.addClass('is-valid');
33343         }
33344         
33345         
33346         this.fireEvent('valid', this);
33347     },
33348     
33349     /**
33350      * Mark this field as invalid
33351      * @param {String} msg The validation message
33352      */
33353     markInvalid : function(msg)
33354     {
33355         if(this.indicator){
33356             this.indicator.removeClass('invisible');
33357             this.indicator.addClass('visible');
33358         }
33359           if (Roo.bootstrap.version == 3) {
33360             this.el.removeClass(this.validClass);
33361             this.el.addClass(this.invalidClass);
33362         } else {
33363             this.el.removeClass('is-valid');
33364             this.el.addClass('is-invalid');
33365         }
33366         
33367         
33368         this.fireEvent('invalid', this, msg);
33369     }
33370     
33371    
33372 });
33373
33374 Roo.apply(Roo.bootstrap.FieldLabel, {
33375     
33376     groups: {},
33377     
33378      /**
33379     * register a FieldLabel Group
33380     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33381     */
33382     register : function(label)
33383     {
33384         if(this.groups.hasOwnProperty(label.target)){
33385             return;
33386         }
33387      
33388         this.groups[label.target] = label;
33389         
33390     },
33391     /**
33392     * fetch a FieldLabel Group based on the target
33393     * @param {string} target
33394     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33395     */
33396     get: function(target) {
33397         if (typeof(this.groups[target]) == 'undefined') {
33398             return false;
33399         }
33400         
33401         return this.groups[target] ;
33402     }
33403 });
33404
33405  
33406
33407  /*
33408  * - LGPL
33409  *
33410  * page DateSplitField.
33411  * 
33412  */
33413
33414
33415 /**
33416  * @class Roo.bootstrap.DateSplitField
33417  * @extends Roo.bootstrap.Component
33418  * Bootstrap DateSplitField class
33419  * @cfg {string} fieldLabel - the label associated
33420  * @cfg {Number} labelWidth set the width of label (0-12)
33421  * @cfg {String} labelAlign (top|left)
33422  * @cfg {Boolean} dayAllowBlank (true|false) default false
33423  * @cfg {Boolean} monthAllowBlank (true|false) default false
33424  * @cfg {Boolean} yearAllowBlank (true|false) default false
33425  * @cfg {string} dayPlaceholder 
33426  * @cfg {string} monthPlaceholder
33427  * @cfg {string} yearPlaceholder
33428  * @cfg {string} dayFormat default 'd'
33429  * @cfg {string} monthFormat default 'm'
33430  * @cfg {string} yearFormat default 'Y'
33431  * @cfg {Number} labellg set the width of label (1-12)
33432  * @cfg {Number} labelmd set the width of label (1-12)
33433  * @cfg {Number} labelsm set the width of label (1-12)
33434  * @cfg {Number} labelxs set the width of label (1-12)
33435
33436  *     
33437  * @constructor
33438  * Create a new DateSplitField
33439  * @param {Object} config The config object
33440  */
33441
33442 Roo.bootstrap.DateSplitField = function(config){
33443     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33444     
33445     this.addEvents({
33446         // raw events
33447          /**
33448          * @event years
33449          * getting the data of years
33450          * @param {Roo.bootstrap.DateSplitField} this
33451          * @param {Object} years
33452          */
33453         "years" : true,
33454         /**
33455          * @event days
33456          * getting the data of days
33457          * @param {Roo.bootstrap.DateSplitField} this
33458          * @param {Object} days
33459          */
33460         "days" : true,
33461         /**
33462          * @event invalid
33463          * Fires after the field has been marked as invalid.
33464          * @param {Roo.form.Field} this
33465          * @param {String} msg The validation message
33466          */
33467         invalid : true,
33468        /**
33469          * @event valid
33470          * Fires after the field has been validated with no errors.
33471          * @param {Roo.form.Field} this
33472          */
33473         valid : true
33474     });
33475 };
33476
33477 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33478     
33479     fieldLabel : '',
33480     labelAlign : 'top',
33481     labelWidth : 3,
33482     dayAllowBlank : false,
33483     monthAllowBlank : false,
33484     yearAllowBlank : false,
33485     dayPlaceholder : '',
33486     monthPlaceholder : '',
33487     yearPlaceholder : '',
33488     dayFormat : 'd',
33489     monthFormat : 'm',
33490     yearFormat : 'Y',
33491     isFormField : true,
33492     labellg : 0,
33493     labelmd : 0,
33494     labelsm : 0,
33495     labelxs : 0,
33496     
33497     getAutoCreate : function()
33498     {
33499         var cfg = {
33500             tag : 'div',
33501             cls : 'row roo-date-split-field-group',
33502             cn : [
33503                 {
33504                     tag : 'input',
33505                     type : 'hidden',
33506                     cls : 'form-hidden-field roo-date-split-field-group-value',
33507                     name : this.name
33508                 }
33509             ]
33510         };
33511         
33512         var labelCls = 'col-md-12';
33513         var contentCls = 'col-md-4';
33514         
33515         if(this.fieldLabel){
33516             
33517             var label = {
33518                 tag : 'div',
33519                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33520                 cn : [
33521                     {
33522                         tag : 'label',
33523                         html : this.fieldLabel
33524                     }
33525                 ]
33526             };
33527             
33528             if(this.labelAlign == 'left'){
33529             
33530                 if(this.labelWidth > 12){
33531                     label.style = "width: " + this.labelWidth + 'px';
33532                 }
33533
33534                 if(this.labelWidth < 13 && this.labelmd == 0){
33535                     this.labelmd = this.labelWidth;
33536                 }
33537
33538                 if(this.labellg > 0){
33539                     labelCls = ' col-lg-' + this.labellg;
33540                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33541                 }
33542
33543                 if(this.labelmd > 0){
33544                     labelCls = ' col-md-' + this.labelmd;
33545                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33546                 }
33547
33548                 if(this.labelsm > 0){
33549                     labelCls = ' col-sm-' + this.labelsm;
33550                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33551                 }
33552
33553                 if(this.labelxs > 0){
33554                     labelCls = ' col-xs-' + this.labelxs;
33555                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33556                 }
33557             }
33558             
33559             label.cls += ' ' + labelCls;
33560             
33561             cfg.cn.push(label);
33562         }
33563         
33564         Roo.each(['day', 'month', 'year'], function(t){
33565             cfg.cn.push({
33566                 tag : 'div',
33567                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33568             });
33569         }, this);
33570         
33571         return cfg;
33572     },
33573     
33574     inputEl: function ()
33575     {
33576         return this.el.select('.roo-date-split-field-group-value', true).first();
33577     },
33578     
33579     onRender : function(ct, position) 
33580     {
33581         var _this = this;
33582         
33583         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33584         
33585         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33586         
33587         this.dayField = new Roo.bootstrap.ComboBox({
33588             allowBlank : this.dayAllowBlank,
33589             alwaysQuery : true,
33590             displayField : 'value',
33591             editable : false,
33592             fieldLabel : '',
33593             forceSelection : true,
33594             mode : 'local',
33595             placeholder : this.dayPlaceholder,
33596             selectOnFocus : true,
33597             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33598             triggerAction : 'all',
33599             typeAhead : true,
33600             valueField : 'value',
33601             store : new Roo.data.SimpleStore({
33602                 data : (function() {    
33603                     var days = [];
33604                     _this.fireEvent('days', _this, days);
33605                     return days;
33606                 })(),
33607                 fields : [ 'value' ]
33608             }),
33609             listeners : {
33610                 select : function (_self, record, index)
33611                 {
33612                     _this.setValue(_this.getValue());
33613                 }
33614             }
33615         });
33616
33617         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33618         
33619         this.monthField = new Roo.bootstrap.MonthField({
33620             after : '<i class=\"fa fa-calendar\"></i>',
33621             allowBlank : this.monthAllowBlank,
33622             placeholder : this.monthPlaceholder,
33623             readOnly : true,
33624             listeners : {
33625                 render : function (_self)
33626                 {
33627                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33628                         e.preventDefault();
33629                         _self.focus();
33630                     });
33631                 },
33632                 select : function (_self, oldvalue, newvalue)
33633                 {
33634                     _this.setValue(_this.getValue());
33635                 }
33636             }
33637         });
33638         
33639         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33640         
33641         this.yearField = new Roo.bootstrap.ComboBox({
33642             allowBlank : this.yearAllowBlank,
33643             alwaysQuery : true,
33644             displayField : 'value',
33645             editable : false,
33646             fieldLabel : '',
33647             forceSelection : true,
33648             mode : 'local',
33649             placeholder : this.yearPlaceholder,
33650             selectOnFocus : true,
33651             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33652             triggerAction : 'all',
33653             typeAhead : true,
33654             valueField : 'value',
33655             store : new Roo.data.SimpleStore({
33656                 data : (function() {
33657                     var years = [];
33658                     _this.fireEvent('years', _this, years);
33659                     return years;
33660                 })(),
33661                 fields : [ 'value' ]
33662             }),
33663             listeners : {
33664                 select : function (_self, record, index)
33665                 {
33666                     _this.setValue(_this.getValue());
33667                 }
33668             }
33669         });
33670
33671         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33672     },
33673     
33674     setValue : function(v, format)
33675     {
33676         this.inputEl.dom.value = v;
33677         
33678         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33679         
33680         var d = Date.parseDate(v, f);
33681         
33682         if(!d){
33683             this.validate();
33684             return;
33685         }
33686         
33687         this.setDay(d.format(this.dayFormat));
33688         this.setMonth(d.format(this.monthFormat));
33689         this.setYear(d.format(this.yearFormat));
33690         
33691         this.validate();
33692         
33693         return;
33694     },
33695     
33696     setDay : function(v)
33697     {
33698         this.dayField.setValue(v);
33699         this.inputEl.dom.value = this.getValue();
33700         this.validate();
33701         return;
33702     },
33703     
33704     setMonth : function(v)
33705     {
33706         this.monthField.setValue(v, true);
33707         this.inputEl.dom.value = this.getValue();
33708         this.validate();
33709         return;
33710     },
33711     
33712     setYear : function(v)
33713     {
33714         this.yearField.setValue(v);
33715         this.inputEl.dom.value = this.getValue();
33716         this.validate();
33717         return;
33718     },
33719     
33720     getDay : function()
33721     {
33722         return this.dayField.getValue();
33723     },
33724     
33725     getMonth : function()
33726     {
33727         return this.monthField.getValue();
33728     },
33729     
33730     getYear : function()
33731     {
33732         return this.yearField.getValue();
33733     },
33734     
33735     getValue : function()
33736     {
33737         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33738         
33739         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33740         
33741         return date;
33742     },
33743     
33744     reset : function()
33745     {
33746         this.setDay('');
33747         this.setMonth('');
33748         this.setYear('');
33749         this.inputEl.dom.value = '';
33750         this.validate();
33751         return;
33752     },
33753     
33754     validate : function()
33755     {
33756         var d = this.dayField.validate();
33757         var m = this.monthField.validate();
33758         var y = this.yearField.validate();
33759         
33760         var valid = true;
33761         
33762         if(
33763                 (!this.dayAllowBlank && !d) ||
33764                 (!this.monthAllowBlank && !m) ||
33765                 (!this.yearAllowBlank && !y)
33766         ){
33767             valid = false;
33768         }
33769         
33770         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33771             return valid;
33772         }
33773         
33774         if(valid){
33775             this.markValid();
33776             return valid;
33777         }
33778         
33779         this.markInvalid();
33780         
33781         return valid;
33782     },
33783     
33784     markValid : function()
33785     {
33786         
33787         var label = this.el.select('label', true).first();
33788         var icon = this.el.select('i.fa-star', true).first();
33789
33790         if(label && icon){
33791             icon.remove();
33792         }
33793         
33794         this.fireEvent('valid', this);
33795     },
33796     
33797      /**
33798      * Mark this field as invalid
33799      * @param {String} msg The validation message
33800      */
33801     markInvalid : function(msg)
33802     {
33803         
33804         var label = this.el.select('label', true).first();
33805         var icon = this.el.select('i.fa-star', true).first();
33806
33807         if(label && !icon){
33808             this.el.select('.roo-date-split-field-label', true).createChild({
33809                 tag : 'i',
33810                 cls : 'text-danger fa fa-lg fa-star',
33811                 tooltip : 'This field is required',
33812                 style : 'margin-right:5px;'
33813             }, label, true);
33814         }
33815         
33816         this.fireEvent('invalid', this, msg);
33817     },
33818     
33819     clearInvalid : function()
33820     {
33821         var label = this.el.select('label', true).first();
33822         var icon = this.el.select('i.fa-star', true).first();
33823
33824         if(label && icon){
33825             icon.remove();
33826         }
33827         
33828         this.fireEvent('valid', this);
33829     },
33830     
33831     getName: function()
33832     {
33833         return this.name;
33834     }
33835     
33836 });
33837
33838  /**
33839  *
33840  * This is based on 
33841  * http://masonry.desandro.com
33842  *
33843  * The idea is to render all the bricks based on vertical width...
33844  *
33845  * The original code extends 'outlayer' - we might need to use that....
33846  * 
33847  */
33848
33849
33850 /**
33851  * @class Roo.bootstrap.LayoutMasonry
33852  * @extends Roo.bootstrap.Component
33853  * Bootstrap Layout Masonry class
33854  * 
33855  * @constructor
33856  * Create a new Element
33857  * @param {Object} config The config object
33858  */
33859
33860 Roo.bootstrap.LayoutMasonry = function(config){
33861     
33862     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33863     
33864     this.bricks = [];
33865     
33866     Roo.bootstrap.LayoutMasonry.register(this);
33867     
33868     this.addEvents({
33869         // raw events
33870         /**
33871          * @event layout
33872          * Fire after layout the items
33873          * @param {Roo.bootstrap.LayoutMasonry} this
33874          * @param {Roo.EventObject} e
33875          */
33876         "layout" : true
33877     });
33878     
33879 };
33880
33881 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33882     
33883     /**
33884      * @cfg {Boolean} isLayoutInstant = no animation?
33885      */   
33886     isLayoutInstant : false, // needed?
33887    
33888     /**
33889      * @cfg {Number} boxWidth  width of the columns
33890      */   
33891     boxWidth : 450,
33892     
33893       /**
33894      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33895      */   
33896     boxHeight : 0,
33897     
33898     /**
33899      * @cfg {Number} padWidth padding below box..
33900      */   
33901     padWidth : 10, 
33902     
33903     /**
33904      * @cfg {Number} gutter gutter width..
33905      */   
33906     gutter : 10,
33907     
33908      /**
33909      * @cfg {Number} maxCols maximum number of columns
33910      */   
33911     
33912     maxCols: 0,
33913     
33914     /**
33915      * @cfg {Boolean} isAutoInitial defalut true
33916      */   
33917     isAutoInitial : true, 
33918     
33919     containerWidth: 0,
33920     
33921     /**
33922      * @cfg {Boolean} isHorizontal defalut false
33923      */   
33924     isHorizontal : false, 
33925
33926     currentSize : null,
33927     
33928     tag: 'div',
33929     
33930     cls: '',
33931     
33932     bricks: null, //CompositeElement
33933     
33934     cols : 1,
33935     
33936     _isLayoutInited : false,
33937     
33938 //    isAlternative : false, // only use for vertical layout...
33939     
33940     /**
33941      * @cfg {Number} alternativePadWidth padding below box..
33942      */   
33943     alternativePadWidth : 50,
33944     
33945     selectedBrick : [],
33946     
33947     getAutoCreate : function(){
33948         
33949         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33950         
33951         var cfg = {
33952             tag: this.tag,
33953             cls: 'blog-masonary-wrapper ' + this.cls,
33954             cn : {
33955                 cls : 'mas-boxes masonary'
33956             }
33957         };
33958         
33959         return cfg;
33960     },
33961     
33962     getChildContainer: function( )
33963     {
33964         if (this.boxesEl) {
33965             return this.boxesEl;
33966         }
33967         
33968         this.boxesEl = this.el.select('.mas-boxes').first();
33969         
33970         return this.boxesEl;
33971     },
33972     
33973     
33974     initEvents : function()
33975     {
33976         var _this = this;
33977         
33978         if(this.isAutoInitial){
33979             Roo.log('hook children rendered');
33980             this.on('childrenrendered', function() {
33981                 Roo.log('children rendered');
33982                 _this.initial();
33983             } ,this);
33984         }
33985     },
33986     
33987     initial : function()
33988     {
33989         this.selectedBrick = [];
33990         
33991         this.currentSize = this.el.getBox(true);
33992         
33993         Roo.EventManager.onWindowResize(this.resize, this); 
33994
33995         if(!this.isAutoInitial){
33996             this.layout();
33997             return;
33998         }
33999         
34000         this.layout();
34001         
34002         return;
34003         //this.layout.defer(500,this);
34004         
34005     },
34006     
34007     resize : function()
34008     {
34009         var cs = this.el.getBox(true);
34010         
34011         if (
34012                 this.currentSize.width == cs.width && 
34013                 this.currentSize.x == cs.x && 
34014                 this.currentSize.height == cs.height && 
34015                 this.currentSize.y == cs.y 
34016         ) {
34017             Roo.log("no change in with or X or Y");
34018             return;
34019         }
34020         
34021         this.currentSize = cs;
34022         
34023         this.layout();
34024         
34025     },
34026     
34027     layout : function()
34028     {   
34029         this._resetLayout();
34030         
34031         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34032         
34033         this.layoutItems( isInstant );
34034       
34035         this._isLayoutInited = true;
34036         
34037         this.fireEvent('layout', this);
34038         
34039     },
34040     
34041     _resetLayout : function()
34042     {
34043         if(this.isHorizontal){
34044             this.horizontalMeasureColumns();
34045             return;
34046         }
34047         
34048         this.verticalMeasureColumns();
34049         
34050     },
34051     
34052     verticalMeasureColumns : function()
34053     {
34054         this.getContainerWidth();
34055         
34056 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34057 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34058 //            return;
34059 //        }
34060         
34061         var boxWidth = this.boxWidth + this.padWidth;
34062         
34063         if(this.containerWidth < this.boxWidth){
34064             boxWidth = this.containerWidth
34065         }
34066         
34067         var containerWidth = this.containerWidth;
34068         
34069         var cols = Math.floor(containerWidth / boxWidth);
34070         
34071         this.cols = Math.max( cols, 1 );
34072         
34073         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34074         
34075         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34076         
34077         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34078         
34079         this.colWidth = boxWidth + avail - this.padWidth;
34080         
34081         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34082         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34083     },
34084     
34085     horizontalMeasureColumns : function()
34086     {
34087         this.getContainerWidth();
34088         
34089         var boxWidth = this.boxWidth;
34090         
34091         if(this.containerWidth < boxWidth){
34092             boxWidth = this.containerWidth;
34093         }
34094         
34095         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34096         
34097         this.el.setHeight(boxWidth);
34098         
34099     },
34100     
34101     getContainerWidth : function()
34102     {
34103         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34104     },
34105     
34106     layoutItems : function( isInstant )
34107     {
34108         Roo.log(this.bricks);
34109         
34110         var items = Roo.apply([], this.bricks);
34111         
34112         if(this.isHorizontal){
34113             this._horizontalLayoutItems( items , isInstant );
34114             return;
34115         }
34116         
34117 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34118 //            this._verticalAlternativeLayoutItems( items , isInstant );
34119 //            return;
34120 //        }
34121         
34122         this._verticalLayoutItems( items , isInstant );
34123         
34124     },
34125     
34126     _verticalLayoutItems : function ( items , isInstant)
34127     {
34128         if ( !items || !items.length ) {
34129             return;
34130         }
34131         
34132         var standard = [
34133             ['xs', 'xs', 'xs', 'tall'],
34134             ['xs', 'xs', 'tall'],
34135             ['xs', 'xs', 'sm'],
34136             ['xs', 'xs', 'xs'],
34137             ['xs', 'tall'],
34138             ['xs', 'sm'],
34139             ['xs', 'xs'],
34140             ['xs'],
34141             
34142             ['sm', 'xs', 'xs'],
34143             ['sm', 'xs'],
34144             ['sm'],
34145             
34146             ['tall', 'xs', 'xs', 'xs'],
34147             ['tall', 'xs', 'xs'],
34148             ['tall', 'xs'],
34149             ['tall']
34150             
34151         ];
34152         
34153         var queue = [];
34154         
34155         var boxes = [];
34156         
34157         var box = [];
34158         
34159         Roo.each(items, function(item, k){
34160             
34161             switch (item.size) {
34162                 // these layouts take up a full box,
34163                 case 'md' :
34164                 case 'md-left' :
34165                 case 'md-right' :
34166                 case 'wide' :
34167                     
34168                     if(box.length){
34169                         boxes.push(box);
34170                         box = [];
34171                     }
34172                     
34173                     boxes.push([item]);
34174                     
34175                     break;
34176                     
34177                 case 'xs' :
34178                 case 'sm' :
34179                 case 'tall' :
34180                     
34181                     box.push(item);
34182                     
34183                     break;
34184                 default :
34185                     break;
34186                     
34187             }
34188             
34189         }, this);
34190         
34191         if(box.length){
34192             boxes.push(box);
34193             box = [];
34194         }
34195         
34196         var filterPattern = function(box, length)
34197         {
34198             if(!box.length){
34199                 return;
34200             }
34201             
34202             var match = false;
34203             
34204             var pattern = box.slice(0, length);
34205             
34206             var format = [];
34207             
34208             Roo.each(pattern, function(i){
34209                 format.push(i.size);
34210             }, this);
34211             
34212             Roo.each(standard, function(s){
34213                 
34214                 if(String(s) != String(format)){
34215                     return;
34216                 }
34217                 
34218                 match = true;
34219                 return false;
34220                 
34221             }, this);
34222             
34223             if(!match && length == 1){
34224                 return;
34225             }
34226             
34227             if(!match){
34228                 filterPattern(box, length - 1);
34229                 return;
34230             }
34231                 
34232             queue.push(pattern);
34233
34234             box = box.slice(length, box.length);
34235
34236             filterPattern(box, 4);
34237
34238             return;
34239             
34240         }
34241         
34242         Roo.each(boxes, function(box, k){
34243             
34244             if(!box.length){
34245                 return;
34246             }
34247             
34248             if(box.length == 1){
34249                 queue.push(box);
34250                 return;
34251             }
34252             
34253             filterPattern(box, 4);
34254             
34255         }, this);
34256         
34257         this._processVerticalLayoutQueue( queue, isInstant );
34258         
34259     },
34260     
34261 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34262 //    {
34263 //        if ( !items || !items.length ) {
34264 //            return;
34265 //        }
34266 //
34267 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34268 //        
34269 //    },
34270     
34271     _horizontalLayoutItems : function ( items , isInstant)
34272     {
34273         if ( !items || !items.length || items.length < 3) {
34274             return;
34275         }
34276         
34277         items.reverse();
34278         
34279         var eItems = items.slice(0, 3);
34280         
34281         items = items.slice(3, items.length);
34282         
34283         var standard = [
34284             ['xs', 'xs', 'xs', 'wide'],
34285             ['xs', 'xs', 'wide'],
34286             ['xs', 'xs', 'sm'],
34287             ['xs', 'xs', 'xs'],
34288             ['xs', 'wide'],
34289             ['xs', 'sm'],
34290             ['xs', 'xs'],
34291             ['xs'],
34292             
34293             ['sm', 'xs', 'xs'],
34294             ['sm', 'xs'],
34295             ['sm'],
34296             
34297             ['wide', 'xs', 'xs', 'xs'],
34298             ['wide', 'xs', 'xs'],
34299             ['wide', 'xs'],
34300             ['wide'],
34301             
34302             ['wide-thin']
34303         ];
34304         
34305         var queue = [];
34306         
34307         var boxes = [];
34308         
34309         var box = [];
34310         
34311         Roo.each(items, function(item, k){
34312             
34313             switch (item.size) {
34314                 case 'md' :
34315                 case 'md-left' :
34316                 case 'md-right' :
34317                 case 'tall' :
34318                     
34319                     if(box.length){
34320                         boxes.push(box);
34321                         box = [];
34322                     }
34323                     
34324                     boxes.push([item]);
34325                     
34326                     break;
34327                     
34328                 case 'xs' :
34329                 case 'sm' :
34330                 case 'wide' :
34331                 case 'wide-thin' :
34332                     
34333                     box.push(item);
34334                     
34335                     break;
34336                 default :
34337                     break;
34338                     
34339             }
34340             
34341         }, this);
34342         
34343         if(box.length){
34344             boxes.push(box);
34345             box = [];
34346         }
34347         
34348         var filterPattern = function(box, length)
34349         {
34350             if(!box.length){
34351                 return;
34352             }
34353             
34354             var match = false;
34355             
34356             var pattern = box.slice(0, length);
34357             
34358             var format = [];
34359             
34360             Roo.each(pattern, function(i){
34361                 format.push(i.size);
34362             }, this);
34363             
34364             Roo.each(standard, function(s){
34365                 
34366                 if(String(s) != String(format)){
34367                     return;
34368                 }
34369                 
34370                 match = true;
34371                 return false;
34372                 
34373             }, this);
34374             
34375             if(!match && length == 1){
34376                 return;
34377             }
34378             
34379             if(!match){
34380                 filterPattern(box, length - 1);
34381                 return;
34382             }
34383                 
34384             queue.push(pattern);
34385
34386             box = box.slice(length, box.length);
34387
34388             filterPattern(box, 4);
34389
34390             return;
34391             
34392         }
34393         
34394         Roo.each(boxes, function(box, k){
34395             
34396             if(!box.length){
34397                 return;
34398             }
34399             
34400             if(box.length == 1){
34401                 queue.push(box);
34402                 return;
34403             }
34404             
34405             filterPattern(box, 4);
34406             
34407         }, this);
34408         
34409         
34410         var prune = [];
34411         
34412         var pos = this.el.getBox(true);
34413         
34414         var minX = pos.x;
34415         
34416         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34417         
34418         var hit_end = false;
34419         
34420         Roo.each(queue, function(box){
34421             
34422             if(hit_end){
34423                 
34424                 Roo.each(box, function(b){
34425                 
34426                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34427                     b.el.hide();
34428
34429                 }, this);
34430
34431                 return;
34432             }
34433             
34434             var mx = 0;
34435             
34436             Roo.each(box, function(b){
34437                 
34438                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34439                 b.el.show();
34440
34441                 mx = Math.max(mx, b.x);
34442                 
34443             }, this);
34444             
34445             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34446             
34447             if(maxX < minX){
34448                 
34449                 Roo.each(box, function(b){
34450                 
34451                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34452                     b.el.hide();
34453                     
34454                 }, this);
34455                 
34456                 hit_end = true;
34457                 
34458                 return;
34459             }
34460             
34461             prune.push(box);
34462             
34463         }, this);
34464         
34465         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34466     },
34467     
34468     /** Sets position of item in DOM
34469     * @param {Element} item
34470     * @param {Number} x - horizontal position
34471     * @param {Number} y - vertical position
34472     * @param {Boolean} isInstant - disables transitions
34473     */
34474     _processVerticalLayoutQueue : function( queue, isInstant )
34475     {
34476         var pos = this.el.getBox(true);
34477         var x = pos.x;
34478         var y = pos.y;
34479         var maxY = [];
34480         
34481         for (var i = 0; i < this.cols; i++){
34482             maxY[i] = pos.y;
34483         }
34484         
34485         Roo.each(queue, function(box, k){
34486             
34487             var col = k % this.cols;
34488             
34489             Roo.each(box, function(b,kk){
34490                 
34491                 b.el.position('absolute');
34492                 
34493                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34494                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34495                 
34496                 if(b.size == 'md-left' || b.size == 'md-right'){
34497                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34498                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34499                 }
34500                 
34501                 b.el.setWidth(width);
34502                 b.el.setHeight(height);
34503                 // iframe?
34504                 b.el.select('iframe',true).setSize(width,height);
34505                 
34506             }, this);
34507             
34508             for (var i = 0; i < this.cols; i++){
34509                 
34510                 if(maxY[i] < maxY[col]){
34511                     col = i;
34512                     continue;
34513                 }
34514                 
34515                 col = Math.min(col, i);
34516                 
34517             }
34518             
34519             x = pos.x + col * (this.colWidth + this.padWidth);
34520             
34521             y = maxY[col];
34522             
34523             var positions = [];
34524             
34525             switch (box.length){
34526                 case 1 :
34527                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34528                     break;
34529                 case 2 :
34530                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34531                     break;
34532                 case 3 :
34533                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34534                     break;
34535                 case 4 :
34536                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34537                     break;
34538                 default :
34539                     break;
34540             }
34541             
34542             Roo.each(box, function(b,kk){
34543                 
34544                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34545                 
34546                 var sz = b.el.getSize();
34547                 
34548                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34549                 
34550             }, this);
34551             
34552         }, this);
34553         
34554         var mY = 0;
34555         
34556         for (var i = 0; i < this.cols; i++){
34557             mY = Math.max(mY, maxY[i]);
34558         }
34559         
34560         this.el.setHeight(mY - pos.y);
34561         
34562     },
34563     
34564 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34565 //    {
34566 //        var pos = this.el.getBox(true);
34567 //        var x = pos.x;
34568 //        var y = pos.y;
34569 //        var maxX = pos.right;
34570 //        
34571 //        var maxHeight = 0;
34572 //        
34573 //        Roo.each(items, function(item, k){
34574 //            
34575 //            var c = k % 2;
34576 //            
34577 //            item.el.position('absolute');
34578 //                
34579 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34580 //
34581 //            item.el.setWidth(width);
34582 //
34583 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34584 //
34585 //            item.el.setHeight(height);
34586 //            
34587 //            if(c == 0){
34588 //                item.el.setXY([x, y], isInstant ? false : true);
34589 //            } else {
34590 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34591 //            }
34592 //            
34593 //            y = y + height + this.alternativePadWidth;
34594 //            
34595 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34596 //            
34597 //        }, this);
34598 //        
34599 //        this.el.setHeight(maxHeight);
34600 //        
34601 //    },
34602     
34603     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34604     {
34605         var pos = this.el.getBox(true);
34606         
34607         var minX = pos.x;
34608         var minY = pos.y;
34609         
34610         var maxX = pos.right;
34611         
34612         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34613         
34614         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34615         
34616         Roo.each(queue, function(box, k){
34617             
34618             Roo.each(box, function(b, kk){
34619                 
34620                 b.el.position('absolute');
34621                 
34622                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34623                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34624                 
34625                 if(b.size == 'md-left' || b.size == 'md-right'){
34626                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34627                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34628                 }
34629                 
34630                 b.el.setWidth(width);
34631                 b.el.setHeight(height);
34632                 
34633             }, this);
34634             
34635             if(!box.length){
34636                 return;
34637             }
34638             
34639             var positions = [];
34640             
34641             switch (box.length){
34642                 case 1 :
34643                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34644                     break;
34645                 case 2 :
34646                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34647                     break;
34648                 case 3 :
34649                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34650                     break;
34651                 case 4 :
34652                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34653                     break;
34654                 default :
34655                     break;
34656             }
34657             
34658             Roo.each(box, function(b,kk){
34659                 
34660                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34661                 
34662                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34663                 
34664             }, this);
34665             
34666         }, this);
34667         
34668     },
34669     
34670     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34671     {
34672         Roo.each(eItems, function(b,k){
34673             
34674             b.size = (k == 0) ? 'sm' : 'xs';
34675             b.x = (k == 0) ? 2 : 1;
34676             b.y = (k == 0) ? 2 : 1;
34677             
34678             b.el.position('absolute');
34679             
34680             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34681                 
34682             b.el.setWidth(width);
34683             
34684             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34685             
34686             b.el.setHeight(height);
34687             
34688         }, this);
34689
34690         var positions = [];
34691         
34692         positions.push({
34693             x : maxX - this.unitWidth * 2 - this.gutter,
34694             y : minY
34695         });
34696         
34697         positions.push({
34698             x : maxX - this.unitWidth,
34699             y : minY + (this.unitWidth + this.gutter) * 2
34700         });
34701         
34702         positions.push({
34703             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34704             y : minY
34705         });
34706         
34707         Roo.each(eItems, function(b,k){
34708             
34709             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34710
34711         }, this);
34712         
34713     },
34714     
34715     getVerticalOneBoxColPositions : function(x, y, box)
34716     {
34717         var pos = [];
34718         
34719         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34720         
34721         if(box[0].size == 'md-left'){
34722             rand = 0;
34723         }
34724         
34725         if(box[0].size == 'md-right'){
34726             rand = 1;
34727         }
34728         
34729         pos.push({
34730             x : x + (this.unitWidth + this.gutter) * rand,
34731             y : y
34732         });
34733         
34734         return pos;
34735     },
34736     
34737     getVerticalTwoBoxColPositions : function(x, y, box)
34738     {
34739         var pos = [];
34740         
34741         if(box[0].size == 'xs'){
34742             
34743             pos.push({
34744                 x : x,
34745                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34746             });
34747
34748             pos.push({
34749                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34750                 y : y
34751             });
34752             
34753             return pos;
34754             
34755         }
34756         
34757         pos.push({
34758             x : x,
34759             y : y
34760         });
34761
34762         pos.push({
34763             x : x + (this.unitWidth + this.gutter) * 2,
34764             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34765         });
34766         
34767         return pos;
34768         
34769     },
34770     
34771     getVerticalThreeBoxColPositions : function(x, y, box)
34772     {
34773         var pos = [];
34774         
34775         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34776             
34777             pos.push({
34778                 x : x,
34779                 y : y
34780             });
34781
34782             pos.push({
34783                 x : x + (this.unitWidth + this.gutter) * 1,
34784                 y : y
34785             });
34786             
34787             pos.push({
34788                 x : x + (this.unitWidth + this.gutter) * 2,
34789                 y : y
34790             });
34791             
34792             return pos;
34793             
34794         }
34795         
34796         if(box[0].size == 'xs' && box[1].size == 'xs'){
34797             
34798             pos.push({
34799                 x : x,
34800                 y : y
34801             });
34802
34803             pos.push({
34804                 x : x,
34805                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34806             });
34807             
34808             pos.push({
34809                 x : x + (this.unitWidth + this.gutter) * 1,
34810                 y : y
34811             });
34812             
34813             return pos;
34814             
34815         }
34816         
34817         pos.push({
34818             x : x,
34819             y : y
34820         });
34821
34822         pos.push({
34823             x : x + (this.unitWidth + this.gutter) * 2,
34824             y : y
34825         });
34826
34827         pos.push({
34828             x : x + (this.unitWidth + this.gutter) * 2,
34829             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34830         });
34831             
34832         return pos;
34833         
34834     },
34835     
34836     getVerticalFourBoxColPositions : function(x, y, box)
34837     {
34838         var pos = [];
34839         
34840         if(box[0].size == 'xs'){
34841             
34842             pos.push({
34843                 x : x,
34844                 y : y
34845             });
34846
34847             pos.push({
34848                 x : x,
34849                 y : y + (this.unitHeight + this.gutter) * 1
34850             });
34851             
34852             pos.push({
34853                 x : x,
34854                 y : y + (this.unitHeight + this.gutter) * 2
34855             });
34856             
34857             pos.push({
34858                 x : x + (this.unitWidth + this.gutter) * 1,
34859                 y : y
34860             });
34861             
34862             return pos;
34863             
34864         }
34865         
34866         pos.push({
34867             x : x,
34868             y : y
34869         });
34870
34871         pos.push({
34872             x : x + (this.unitWidth + this.gutter) * 2,
34873             y : y
34874         });
34875
34876         pos.push({
34877             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34878             y : y + (this.unitHeight + this.gutter) * 1
34879         });
34880
34881         pos.push({
34882             x : x + (this.unitWidth + this.gutter) * 2,
34883             y : y + (this.unitWidth + this.gutter) * 2
34884         });
34885
34886         return pos;
34887         
34888     },
34889     
34890     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34891     {
34892         var pos = [];
34893         
34894         if(box[0].size == 'md-left'){
34895             pos.push({
34896                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34897                 y : minY
34898             });
34899             
34900             return pos;
34901         }
34902         
34903         if(box[0].size == 'md-right'){
34904             pos.push({
34905                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34906                 y : minY + (this.unitWidth + this.gutter) * 1
34907             });
34908             
34909             return pos;
34910         }
34911         
34912         var rand = Math.floor(Math.random() * (4 - box[0].y));
34913         
34914         pos.push({
34915             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34916             y : minY + (this.unitWidth + this.gutter) * rand
34917         });
34918         
34919         return pos;
34920         
34921     },
34922     
34923     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34924     {
34925         var pos = [];
34926         
34927         if(box[0].size == 'xs'){
34928             
34929             pos.push({
34930                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34931                 y : minY
34932             });
34933
34934             pos.push({
34935                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34936                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34937             });
34938             
34939             return pos;
34940             
34941         }
34942         
34943         pos.push({
34944             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34945             y : minY
34946         });
34947
34948         pos.push({
34949             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34950             y : minY + (this.unitWidth + this.gutter) * 2
34951         });
34952         
34953         return pos;
34954         
34955     },
34956     
34957     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34958     {
34959         var pos = [];
34960         
34961         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34962             
34963             pos.push({
34964                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34965                 y : minY
34966             });
34967
34968             pos.push({
34969                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970                 y : minY + (this.unitWidth + this.gutter) * 1
34971             });
34972             
34973             pos.push({
34974                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34975                 y : minY + (this.unitWidth + this.gutter) * 2
34976             });
34977             
34978             return pos;
34979             
34980         }
34981         
34982         if(box[0].size == 'xs' && box[1].size == 'xs'){
34983             
34984             pos.push({
34985                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986                 y : minY
34987             });
34988
34989             pos.push({
34990                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34991                 y : minY
34992             });
34993             
34994             pos.push({
34995                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34996                 y : minY + (this.unitWidth + this.gutter) * 1
34997             });
34998             
34999             return pos;
35000             
35001         }
35002         
35003         pos.push({
35004             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35005             y : minY
35006         });
35007
35008         pos.push({
35009             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35010             y : minY + (this.unitWidth + this.gutter) * 2
35011         });
35012
35013         pos.push({
35014             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35015             y : minY + (this.unitWidth + this.gutter) * 2
35016         });
35017             
35018         return pos;
35019         
35020     },
35021     
35022     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35023     {
35024         var pos = [];
35025         
35026         if(box[0].size == 'xs'){
35027             
35028             pos.push({
35029                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35030                 y : minY
35031             });
35032
35033             pos.push({
35034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35035                 y : minY
35036             });
35037             
35038             pos.push({
35039                 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),
35040                 y : minY
35041             });
35042             
35043             pos.push({
35044                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35045                 y : minY + (this.unitWidth + this.gutter) * 1
35046             });
35047             
35048             return pos;
35049             
35050         }
35051         
35052         pos.push({
35053             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35054             y : minY
35055         });
35056         
35057         pos.push({
35058             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35059             y : minY + (this.unitWidth + this.gutter) * 2
35060         });
35061         
35062         pos.push({
35063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35064             y : minY + (this.unitWidth + this.gutter) * 2
35065         });
35066         
35067         pos.push({
35068             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),
35069             y : minY + (this.unitWidth + this.gutter) * 2
35070         });
35071
35072         return pos;
35073         
35074     },
35075     
35076     /**
35077     * remove a Masonry Brick
35078     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35079     */
35080     removeBrick : function(brick_id)
35081     {
35082         if (!brick_id) {
35083             return;
35084         }
35085         
35086         for (var i = 0; i<this.bricks.length; i++) {
35087             if (this.bricks[i].id == brick_id) {
35088                 this.bricks.splice(i,1);
35089                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35090                 this.initial();
35091             }
35092         }
35093     },
35094     
35095     /**
35096     * adds a Masonry Brick
35097     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35098     */
35099     addBrick : function(cfg)
35100     {
35101         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35102         //this.register(cn);
35103         cn.parentId = this.id;
35104         cn.render(this.el);
35105         return cn;
35106     },
35107     
35108     /**
35109     * register a Masonry Brick
35110     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35111     */
35112     
35113     register : function(brick)
35114     {
35115         this.bricks.push(brick);
35116         brick.masonryId = this.id;
35117     },
35118     
35119     /**
35120     * clear all the Masonry Brick
35121     */
35122     clearAll : function()
35123     {
35124         this.bricks = [];
35125         //this.getChildContainer().dom.innerHTML = "";
35126         this.el.dom.innerHTML = '';
35127     },
35128     
35129     getSelected : function()
35130     {
35131         if (!this.selectedBrick) {
35132             return false;
35133         }
35134         
35135         return this.selectedBrick;
35136     }
35137 });
35138
35139 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35140     
35141     groups: {},
35142      /**
35143     * register a Masonry Layout
35144     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35145     */
35146     
35147     register : function(layout)
35148     {
35149         this.groups[layout.id] = layout;
35150     },
35151     /**
35152     * fetch a  Masonry Layout based on the masonry layout ID
35153     * @param {string} the masonry layout to add
35154     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35155     */
35156     
35157     get: function(layout_id) {
35158         if (typeof(this.groups[layout_id]) == 'undefined') {
35159             return false;
35160         }
35161         return this.groups[layout_id] ;
35162     }
35163     
35164     
35165     
35166 });
35167
35168  
35169
35170  /**
35171  *
35172  * This is based on 
35173  * http://masonry.desandro.com
35174  *
35175  * The idea is to render all the bricks based on vertical width...
35176  *
35177  * The original code extends 'outlayer' - we might need to use that....
35178  * 
35179  */
35180
35181
35182 /**
35183  * @class Roo.bootstrap.LayoutMasonryAuto
35184  * @extends Roo.bootstrap.Component
35185  * Bootstrap Layout Masonry class
35186  * 
35187  * @constructor
35188  * Create a new Element
35189  * @param {Object} config The config object
35190  */
35191
35192 Roo.bootstrap.LayoutMasonryAuto = function(config){
35193     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35194 };
35195
35196 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35197     
35198       /**
35199      * @cfg {Boolean} isFitWidth  - resize the width..
35200      */   
35201     isFitWidth : false,  // options..
35202     /**
35203      * @cfg {Boolean} isOriginLeft = left align?
35204      */   
35205     isOriginLeft : true,
35206     /**
35207      * @cfg {Boolean} isOriginTop = top align?
35208      */   
35209     isOriginTop : false,
35210     /**
35211      * @cfg {Boolean} isLayoutInstant = no animation?
35212      */   
35213     isLayoutInstant : false, // needed?
35214     /**
35215      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35216      */   
35217     isResizingContainer : true,
35218     /**
35219      * @cfg {Number} columnWidth  width of the columns 
35220      */   
35221     
35222     columnWidth : 0,
35223     
35224     /**
35225      * @cfg {Number} maxCols maximum number of columns
35226      */   
35227     
35228     maxCols: 0,
35229     /**
35230      * @cfg {Number} padHeight padding below box..
35231      */   
35232     
35233     padHeight : 10, 
35234     
35235     /**
35236      * @cfg {Boolean} isAutoInitial defalut true
35237      */   
35238     
35239     isAutoInitial : true, 
35240     
35241     // private?
35242     gutter : 0,
35243     
35244     containerWidth: 0,
35245     initialColumnWidth : 0,
35246     currentSize : null,
35247     
35248     colYs : null, // array.
35249     maxY : 0,
35250     padWidth: 10,
35251     
35252     
35253     tag: 'div',
35254     cls: '',
35255     bricks: null, //CompositeElement
35256     cols : 0, // array?
35257     // element : null, // wrapped now this.el
35258     _isLayoutInited : null, 
35259     
35260     
35261     getAutoCreate : function(){
35262         
35263         var cfg = {
35264             tag: this.tag,
35265             cls: 'blog-masonary-wrapper ' + this.cls,
35266             cn : {
35267                 cls : 'mas-boxes masonary'
35268             }
35269         };
35270         
35271         return cfg;
35272     },
35273     
35274     getChildContainer: function( )
35275     {
35276         if (this.boxesEl) {
35277             return this.boxesEl;
35278         }
35279         
35280         this.boxesEl = this.el.select('.mas-boxes').first();
35281         
35282         return this.boxesEl;
35283     },
35284     
35285     
35286     initEvents : function()
35287     {
35288         var _this = this;
35289         
35290         if(this.isAutoInitial){
35291             Roo.log('hook children rendered');
35292             this.on('childrenrendered', function() {
35293                 Roo.log('children rendered');
35294                 _this.initial();
35295             } ,this);
35296         }
35297         
35298     },
35299     
35300     initial : function()
35301     {
35302         this.reloadItems();
35303
35304         this.currentSize = this.el.getBox(true);
35305
35306         /// was window resize... - let's see if this works..
35307         Roo.EventManager.onWindowResize(this.resize, this); 
35308
35309         if(!this.isAutoInitial){
35310             this.layout();
35311             return;
35312         }
35313         
35314         this.layout.defer(500,this);
35315     },
35316     
35317     reloadItems: function()
35318     {
35319         this.bricks = this.el.select('.masonry-brick', true);
35320         
35321         this.bricks.each(function(b) {
35322             //Roo.log(b.getSize());
35323             if (!b.attr('originalwidth')) {
35324                 b.attr('originalwidth',  b.getSize().width);
35325             }
35326             
35327         });
35328         
35329         Roo.log(this.bricks.elements.length);
35330     },
35331     
35332     resize : function()
35333     {
35334         Roo.log('resize');
35335         var cs = this.el.getBox(true);
35336         
35337         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35338             Roo.log("no change in with or X");
35339             return;
35340         }
35341         this.currentSize = cs;
35342         this.layout();
35343     },
35344     
35345     layout : function()
35346     {
35347          Roo.log('layout');
35348         this._resetLayout();
35349         //this._manageStamps();
35350       
35351         // don't animate first layout
35352         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35353         this.layoutItems( isInstant );
35354       
35355         // flag for initalized
35356         this._isLayoutInited = true;
35357     },
35358     
35359     layoutItems : function( isInstant )
35360     {
35361         //var items = this._getItemsForLayout( this.items );
35362         // original code supports filtering layout items.. we just ignore it..
35363         
35364         this._layoutItems( this.bricks , isInstant );
35365       
35366         this._postLayout();
35367     },
35368     _layoutItems : function ( items , isInstant)
35369     {
35370        //this.fireEvent( 'layout', this, items );
35371     
35372
35373         if ( !items || !items.elements.length ) {
35374           // no items, emit event with empty array
35375             return;
35376         }
35377
35378         var queue = [];
35379         items.each(function(item) {
35380             Roo.log("layout item");
35381             Roo.log(item);
35382             // get x/y object from method
35383             var position = this._getItemLayoutPosition( item );
35384             // enqueue
35385             position.item = item;
35386             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35387             queue.push( position );
35388         }, this);
35389       
35390         this._processLayoutQueue( queue );
35391     },
35392     /** Sets position of item in DOM
35393     * @param {Element} item
35394     * @param {Number} x - horizontal position
35395     * @param {Number} y - vertical position
35396     * @param {Boolean} isInstant - disables transitions
35397     */
35398     _processLayoutQueue : function( queue )
35399     {
35400         for ( var i=0, len = queue.length; i < len; i++ ) {
35401             var obj = queue[i];
35402             obj.item.position('absolute');
35403             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35404         }
35405     },
35406       
35407     
35408     /**
35409     * Any logic you want to do after each layout,
35410     * i.e. size the container
35411     */
35412     _postLayout : function()
35413     {
35414         this.resizeContainer();
35415     },
35416     
35417     resizeContainer : function()
35418     {
35419         if ( !this.isResizingContainer ) {
35420             return;
35421         }
35422         var size = this._getContainerSize();
35423         if ( size ) {
35424             this.el.setSize(size.width,size.height);
35425             this.boxesEl.setSize(size.width,size.height);
35426         }
35427     },
35428     
35429     
35430     
35431     _resetLayout : function()
35432     {
35433         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35434         this.colWidth = this.el.getWidth();
35435         //this.gutter = this.el.getWidth(); 
35436         
35437         this.measureColumns();
35438
35439         // reset column Y
35440         var i = this.cols;
35441         this.colYs = [];
35442         while (i--) {
35443             this.colYs.push( 0 );
35444         }
35445     
35446         this.maxY = 0;
35447     },
35448
35449     measureColumns : function()
35450     {
35451         this.getContainerWidth();
35452       // if columnWidth is 0, default to outerWidth of first item
35453         if ( !this.columnWidth ) {
35454             var firstItem = this.bricks.first();
35455             Roo.log(firstItem);
35456             this.columnWidth  = this.containerWidth;
35457             if (firstItem && firstItem.attr('originalwidth') ) {
35458                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35459             }
35460             // columnWidth fall back to item of first element
35461             Roo.log("set column width?");
35462                         this.initialColumnWidth = this.columnWidth  ;
35463
35464             // if first elem has no width, default to size of container
35465             
35466         }
35467         
35468         
35469         if (this.initialColumnWidth) {
35470             this.columnWidth = this.initialColumnWidth;
35471         }
35472         
35473         
35474             
35475         // column width is fixed at the top - however if container width get's smaller we should
35476         // reduce it...
35477         
35478         // this bit calcs how man columns..
35479             
35480         var columnWidth = this.columnWidth += this.gutter;
35481       
35482         // calculate columns
35483         var containerWidth = this.containerWidth + this.gutter;
35484         
35485         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35486         // fix rounding errors, typically with gutters
35487         var excess = columnWidth - containerWidth % columnWidth;
35488         
35489         
35490         // if overshoot is less than a pixel, round up, otherwise floor it
35491         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35492         cols = Math[ mathMethod ]( cols );
35493         this.cols = Math.max( cols, 1 );
35494         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35495         
35496          // padding positioning..
35497         var totalColWidth = this.cols * this.columnWidth;
35498         var padavail = this.containerWidth - totalColWidth;
35499         // so for 2 columns - we need 3 'pads'
35500         
35501         var padNeeded = (1+this.cols) * this.padWidth;
35502         
35503         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35504         
35505         this.columnWidth += padExtra
35506         //this.padWidth = Math.floor(padavail /  ( this.cols));
35507         
35508         // adjust colum width so that padding is fixed??
35509         
35510         // we have 3 columns ... total = width * 3
35511         // we have X left over... that should be used by 
35512         
35513         //if (this.expandC) {
35514             
35515         //}
35516         
35517         
35518         
35519     },
35520     
35521     getContainerWidth : function()
35522     {
35523        /* // container is parent if fit width
35524         var container = this.isFitWidth ? this.element.parentNode : this.element;
35525         // check that this.size and size are there
35526         // IE8 triggers resize on body size change, so they might not be
35527         
35528         var size = getSize( container );  //FIXME
35529         this.containerWidth = size && size.innerWidth; //FIXME
35530         */
35531          
35532         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35533         
35534     },
35535     
35536     _getItemLayoutPosition : function( item )  // what is item?
35537     {
35538         // we resize the item to our columnWidth..
35539       
35540         item.setWidth(this.columnWidth);
35541         item.autoBoxAdjust  = false;
35542         
35543         var sz = item.getSize();
35544  
35545         // how many columns does this brick span
35546         var remainder = this.containerWidth % this.columnWidth;
35547         
35548         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35549         // round if off by 1 pixel, otherwise use ceil
35550         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35551         colSpan = Math.min( colSpan, this.cols );
35552         
35553         // normally this should be '1' as we dont' currently allow multi width columns..
35554         
35555         var colGroup = this._getColGroup( colSpan );
35556         // get the minimum Y value from the columns
35557         var minimumY = Math.min.apply( Math, colGroup );
35558         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35559         
35560         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35561          
35562         // position the brick
35563         var position = {
35564             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35565             y: this.currentSize.y + minimumY + this.padHeight
35566         };
35567         
35568         Roo.log(position);
35569         // apply setHeight to necessary columns
35570         var setHeight = minimumY + sz.height + this.padHeight;
35571         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35572         
35573         var setSpan = this.cols + 1 - colGroup.length;
35574         for ( var i = 0; i < setSpan; i++ ) {
35575           this.colYs[ shortColIndex + i ] = setHeight ;
35576         }
35577       
35578         return position;
35579     },
35580     
35581     /**
35582      * @param {Number} colSpan - number of columns the element spans
35583      * @returns {Array} colGroup
35584      */
35585     _getColGroup : function( colSpan )
35586     {
35587         if ( colSpan < 2 ) {
35588           // if brick spans only one column, use all the column Ys
35589           return this.colYs;
35590         }
35591       
35592         var colGroup = [];
35593         // how many different places could this brick fit horizontally
35594         var groupCount = this.cols + 1 - colSpan;
35595         // for each group potential horizontal position
35596         for ( var i = 0; i < groupCount; i++ ) {
35597           // make an array of colY values for that one group
35598           var groupColYs = this.colYs.slice( i, i + colSpan );
35599           // and get the max value of the array
35600           colGroup[i] = Math.max.apply( Math, groupColYs );
35601         }
35602         return colGroup;
35603     },
35604     /*
35605     _manageStamp : function( stamp )
35606     {
35607         var stampSize =  stamp.getSize();
35608         var offset = stamp.getBox();
35609         // get the columns that this stamp affects
35610         var firstX = this.isOriginLeft ? offset.x : offset.right;
35611         var lastX = firstX + stampSize.width;
35612         var firstCol = Math.floor( firstX / this.columnWidth );
35613         firstCol = Math.max( 0, firstCol );
35614         
35615         var lastCol = Math.floor( lastX / this.columnWidth );
35616         // lastCol should not go over if multiple of columnWidth #425
35617         lastCol -= lastX % this.columnWidth ? 0 : 1;
35618         lastCol = Math.min( this.cols - 1, lastCol );
35619         
35620         // set colYs to bottom of the stamp
35621         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35622             stampSize.height;
35623             
35624         for ( var i = firstCol; i <= lastCol; i++ ) {
35625           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35626         }
35627     },
35628     */
35629     
35630     _getContainerSize : function()
35631     {
35632         this.maxY = Math.max.apply( Math, this.colYs );
35633         var size = {
35634             height: this.maxY
35635         };
35636       
35637         if ( this.isFitWidth ) {
35638             size.width = this._getContainerFitWidth();
35639         }
35640       
35641         return size;
35642     },
35643     
35644     _getContainerFitWidth : function()
35645     {
35646         var unusedCols = 0;
35647         // count unused columns
35648         var i = this.cols;
35649         while ( --i ) {
35650           if ( this.colYs[i] !== 0 ) {
35651             break;
35652           }
35653           unusedCols++;
35654         }
35655         // fit container to columns that have been used
35656         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35657     },
35658     
35659     needsResizeLayout : function()
35660     {
35661         var previousWidth = this.containerWidth;
35662         this.getContainerWidth();
35663         return previousWidth !== this.containerWidth;
35664     }
35665  
35666 });
35667
35668  
35669
35670  /*
35671  * - LGPL
35672  *
35673  * element
35674  * 
35675  */
35676
35677 /**
35678  * @class Roo.bootstrap.MasonryBrick
35679  * @extends Roo.bootstrap.Component
35680  * Bootstrap MasonryBrick class
35681  * 
35682  * @constructor
35683  * Create a new MasonryBrick
35684  * @param {Object} config The config object
35685  */
35686
35687 Roo.bootstrap.MasonryBrick = function(config){
35688     
35689     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35690     
35691     Roo.bootstrap.MasonryBrick.register(this);
35692     
35693     this.addEvents({
35694         // raw events
35695         /**
35696          * @event click
35697          * When a MasonryBrick is clcik
35698          * @param {Roo.bootstrap.MasonryBrick} this
35699          * @param {Roo.EventObject} e
35700          */
35701         "click" : true
35702     });
35703 };
35704
35705 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35706     
35707     /**
35708      * @cfg {String} title
35709      */   
35710     title : '',
35711     /**
35712      * @cfg {String} html
35713      */   
35714     html : '',
35715     /**
35716      * @cfg {String} bgimage
35717      */   
35718     bgimage : '',
35719     /**
35720      * @cfg {String} videourl
35721      */   
35722     videourl : '',
35723     /**
35724      * @cfg {String} cls
35725      */   
35726     cls : '',
35727     /**
35728      * @cfg {String} href
35729      */   
35730     href : '',
35731     /**
35732      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35733      */   
35734     size : 'xs',
35735     
35736     /**
35737      * @cfg {String} placetitle (center|bottom)
35738      */   
35739     placetitle : '',
35740     
35741     /**
35742      * @cfg {Boolean} isFitContainer defalut true
35743      */   
35744     isFitContainer : true, 
35745     
35746     /**
35747      * @cfg {Boolean} preventDefault defalut false
35748      */   
35749     preventDefault : false, 
35750     
35751     /**
35752      * @cfg {Boolean} inverse defalut false
35753      */   
35754     maskInverse : false, 
35755     
35756     getAutoCreate : function()
35757     {
35758         if(!this.isFitContainer){
35759             return this.getSplitAutoCreate();
35760         }
35761         
35762         var cls = 'masonry-brick masonry-brick-full';
35763         
35764         if(this.href.length){
35765             cls += ' masonry-brick-link';
35766         }
35767         
35768         if(this.bgimage.length){
35769             cls += ' masonry-brick-image';
35770         }
35771         
35772         if(this.maskInverse){
35773             cls += ' mask-inverse';
35774         }
35775         
35776         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35777             cls += ' enable-mask';
35778         }
35779         
35780         if(this.size){
35781             cls += ' masonry-' + this.size + '-brick';
35782         }
35783         
35784         if(this.placetitle.length){
35785             
35786             switch (this.placetitle) {
35787                 case 'center' :
35788                     cls += ' masonry-center-title';
35789                     break;
35790                 case 'bottom' :
35791                     cls += ' masonry-bottom-title';
35792                     break;
35793                 default:
35794                     break;
35795             }
35796             
35797         } else {
35798             if(!this.html.length && !this.bgimage.length){
35799                 cls += ' masonry-center-title';
35800             }
35801
35802             if(!this.html.length && this.bgimage.length){
35803                 cls += ' masonry-bottom-title';
35804             }
35805         }
35806         
35807         if(this.cls){
35808             cls += ' ' + this.cls;
35809         }
35810         
35811         var cfg = {
35812             tag: (this.href.length) ? 'a' : 'div',
35813             cls: cls,
35814             cn: [
35815                 {
35816                     tag: 'div',
35817                     cls: 'masonry-brick-mask'
35818                 },
35819                 {
35820                     tag: 'div',
35821                     cls: 'masonry-brick-paragraph',
35822                     cn: []
35823                 }
35824             ]
35825         };
35826         
35827         if(this.href.length){
35828             cfg.href = this.href;
35829         }
35830         
35831         var cn = cfg.cn[1].cn;
35832         
35833         if(this.title.length){
35834             cn.push({
35835                 tag: 'h4',
35836                 cls: 'masonry-brick-title',
35837                 html: this.title
35838             });
35839         }
35840         
35841         if(this.html.length){
35842             cn.push({
35843                 tag: 'p',
35844                 cls: 'masonry-brick-text',
35845                 html: this.html
35846             });
35847         }
35848         
35849         if (!this.title.length && !this.html.length) {
35850             cfg.cn[1].cls += ' hide';
35851         }
35852         
35853         if(this.bgimage.length){
35854             cfg.cn.push({
35855                 tag: 'img',
35856                 cls: 'masonry-brick-image-view',
35857                 src: this.bgimage
35858             });
35859         }
35860         
35861         if(this.videourl.length){
35862             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35863             // youtube support only?
35864             cfg.cn.push({
35865                 tag: 'iframe',
35866                 cls: 'masonry-brick-image-view',
35867                 src: vurl,
35868                 frameborder : 0,
35869                 allowfullscreen : true
35870             });
35871         }
35872         
35873         return cfg;
35874         
35875     },
35876     
35877     getSplitAutoCreate : function()
35878     {
35879         var cls = 'masonry-brick masonry-brick-split';
35880         
35881         if(this.href.length){
35882             cls += ' masonry-brick-link';
35883         }
35884         
35885         if(this.bgimage.length){
35886             cls += ' masonry-brick-image';
35887         }
35888         
35889         if(this.size){
35890             cls += ' masonry-' + this.size + '-brick';
35891         }
35892         
35893         switch (this.placetitle) {
35894             case 'center' :
35895                 cls += ' masonry-center-title';
35896                 break;
35897             case 'bottom' :
35898                 cls += ' masonry-bottom-title';
35899                 break;
35900             default:
35901                 if(!this.bgimage.length){
35902                     cls += ' masonry-center-title';
35903                 }
35904
35905                 if(this.bgimage.length){
35906                     cls += ' masonry-bottom-title';
35907                 }
35908                 break;
35909         }
35910         
35911         if(this.cls){
35912             cls += ' ' + this.cls;
35913         }
35914         
35915         var cfg = {
35916             tag: (this.href.length) ? 'a' : 'div',
35917             cls: cls,
35918             cn: [
35919                 {
35920                     tag: 'div',
35921                     cls: 'masonry-brick-split-head',
35922                     cn: [
35923                         {
35924                             tag: 'div',
35925                             cls: 'masonry-brick-paragraph',
35926                             cn: []
35927                         }
35928                     ]
35929                 },
35930                 {
35931                     tag: 'div',
35932                     cls: 'masonry-brick-split-body',
35933                     cn: []
35934                 }
35935             ]
35936         };
35937         
35938         if(this.href.length){
35939             cfg.href = this.href;
35940         }
35941         
35942         if(this.title.length){
35943             cfg.cn[0].cn[0].cn.push({
35944                 tag: 'h4',
35945                 cls: 'masonry-brick-title',
35946                 html: this.title
35947             });
35948         }
35949         
35950         if(this.html.length){
35951             cfg.cn[1].cn.push({
35952                 tag: 'p',
35953                 cls: 'masonry-brick-text',
35954                 html: this.html
35955             });
35956         }
35957
35958         if(this.bgimage.length){
35959             cfg.cn[0].cn.push({
35960                 tag: 'img',
35961                 cls: 'masonry-brick-image-view',
35962                 src: this.bgimage
35963             });
35964         }
35965         
35966         if(this.videourl.length){
35967             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35968             // youtube support only?
35969             cfg.cn[0].cn.cn.push({
35970                 tag: 'iframe',
35971                 cls: 'masonry-brick-image-view',
35972                 src: vurl,
35973                 frameborder : 0,
35974                 allowfullscreen : true
35975             });
35976         }
35977         
35978         return cfg;
35979     },
35980     
35981     initEvents: function() 
35982     {
35983         switch (this.size) {
35984             case 'xs' :
35985                 this.x = 1;
35986                 this.y = 1;
35987                 break;
35988             case 'sm' :
35989                 this.x = 2;
35990                 this.y = 2;
35991                 break;
35992             case 'md' :
35993             case 'md-left' :
35994             case 'md-right' :
35995                 this.x = 3;
35996                 this.y = 3;
35997                 break;
35998             case 'tall' :
35999                 this.x = 2;
36000                 this.y = 3;
36001                 break;
36002             case 'wide' :
36003                 this.x = 3;
36004                 this.y = 2;
36005                 break;
36006             case 'wide-thin' :
36007                 this.x = 3;
36008                 this.y = 1;
36009                 break;
36010                         
36011             default :
36012                 break;
36013         }
36014         
36015         if(Roo.isTouch){
36016             this.el.on('touchstart', this.onTouchStart, this);
36017             this.el.on('touchmove', this.onTouchMove, this);
36018             this.el.on('touchend', this.onTouchEnd, this);
36019             this.el.on('contextmenu', this.onContextMenu, this);
36020         } else {
36021             this.el.on('mouseenter'  ,this.enter, this);
36022             this.el.on('mouseleave', this.leave, this);
36023             this.el.on('click', this.onClick, this);
36024         }
36025         
36026         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36027             this.parent().bricks.push(this);   
36028         }
36029         
36030     },
36031     
36032     onClick: function(e, el)
36033     {
36034         var time = this.endTimer - this.startTimer;
36035         // Roo.log(e.preventDefault());
36036         if(Roo.isTouch){
36037             if(time > 1000){
36038                 e.preventDefault();
36039                 return;
36040             }
36041         }
36042         
36043         if(!this.preventDefault){
36044             return;
36045         }
36046         
36047         e.preventDefault();
36048         
36049         if (this.activeClass != '') {
36050             this.selectBrick();
36051         }
36052         
36053         this.fireEvent('click', this, e);
36054     },
36055     
36056     enter: function(e, el)
36057     {
36058         e.preventDefault();
36059         
36060         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36061             return;
36062         }
36063         
36064         if(this.bgimage.length && this.html.length){
36065             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36066         }
36067     },
36068     
36069     leave: function(e, el)
36070     {
36071         e.preventDefault();
36072         
36073         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36074             return;
36075         }
36076         
36077         if(this.bgimage.length && this.html.length){
36078             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36079         }
36080     },
36081     
36082     onTouchStart: function(e, el)
36083     {
36084 //        e.preventDefault();
36085         
36086         this.touchmoved = false;
36087         
36088         if(!this.isFitContainer){
36089             return;
36090         }
36091         
36092         if(!this.bgimage.length || !this.html.length){
36093             return;
36094         }
36095         
36096         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36097         
36098         this.timer = new Date().getTime();
36099         
36100     },
36101     
36102     onTouchMove: function(e, el)
36103     {
36104         this.touchmoved = true;
36105     },
36106     
36107     onContextMenu : function(e,el)
36108     {
36109         e.preventDefault();
36110         e.stopPropagation();
36111         return false;
36112     },
36113     
36114     onTouchEnd: function(e, el)
36115     {
36116 //        e.preventDefault();
36117         
36118         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36119         
36120             this.leave(e,el);
36121             
36122             return;
36123         }
36124         
36125         if(!this.bgimage.length || !this.html.length){
36126             
36127             if(this.href.length){
36128                 window.location.href = this.href;
36129             }
36130             
36131             return;
36132         }
36133         
36134         if(!this.isFitContainer){
36135             return;
36136         }
36137         
36138         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36139         
36140         window.location.href = this.href;
36141     },
36142     
36143     //selection on single brick only
36144     selectBrick : function() {
36145         
36146         if (!this.parentId) {
36147             return;
36148         }
36149         
36150         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36151         var index = m.selectedBrick.indexOf(this.id);
36152         
36153         if ( index > -1) {
36154             m.selectedBrick.splice(index,1);
36155             this.el.removeClass(this.activeClass);
36156             return;
36157         }
36158         
36159         for(var i = 0; i < m.selectedBrick.length; i++) {
36160             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36161             b.el.removeClass(b.activeClass);
36162         }
36163         
36164         m.selectedBrick = [];
36165         
36166         m.selectedBrick.push(this.id);
36167         this.el.addClass(this.activeClass);
36168         return;
36169     },
36170     
36171     isSelected : function(){
36172         return this.el.hasClass(this.activeClass);
36173         
36174     }
36175 });
36176
36177 Roo.apply(Roo.bootstrap.MasonryBrick, {
36178     
36179     //groups: {},
36180     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36181      /**
36182     * register a Masonry Brick
36183     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36184     */
36185     
36186     register : function(brick)
36187     {
36188         //this.groups[brick.id] = brick;
36189         this.groups.add(brick.id, brick);
36190     },
36191     /**
36192     * fetch a  masonry brick based on the masonry brick ID
36193     * @param {string} the masonry brick to add
36194     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36195     */
36196     
36197     get: function(brick_id) 
36198     {
36199         // if (typeof(this.groups[brick_id]) == 'undefined') {
36200         //     return false;
36201         // }
36202         // return this.groups[brick_id] ;
36203         
36204         if(this.groups.key(brick_id)) {
36205             return this.groups.key(brick_id);
36206         }
36207         
36208         return false;
36209     }
36210     
36211     
36212     
36213 });
36214
36215  /*
36216  * - LGPL
36217  *
36218  * element
36219  * 
36220  */
36221
36222 /**
36223  * @class Roo.bootstrap.Brick
36224  * @extends Roo.bootstrap.Component
36225  * Bootstrap Brick class
36226  * 
36227  * @constructor
36228  * Create a new Brick
36229  * @param {Object} config The config object
36230  */
36231
36232 Roo.bootstrap.Brick = function(config){
36233     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36234     
36235     this.addEvents({
36236         // raw events
36237         /**
36238          * @event click
36239          * When a Brick is click
36240          * @param {Roo.bootstrap.Brick} this
36241          * @param {Roo.EventObject} e
36242          */
36243         "click" : true
36244     });
36245 };
36246
36247 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36248     
36249     /**
36250      * @cfg {String} title
36251      */   
36252     title : '',
36253     /**
36254      * @cfg {String} html
36255      */   
36256     html : '',
36257     /**
36258      * @cfg {String} bgimage
36259      */   
36260     bgimage : '',
36261     /**
36262      * @cfg {String} cls
36263      */   
36264     cls : '',
36265     /**
36266      * @cfg {String} href
36267      */   
36268     href : '',
36269     /**
36270      * @cfg {String} video
36271      */   
36272     video : '',
36273     /**
36274      * @cfg {Boolean} square
36275      */   
36276     square : true,
36277     
36278     getAutoCreate : function()
36279     {
36280         var cls = 'roo-brick';
36281         
36282         if(this.href.length){
36283             cls += ' roo-brick-link';
36284         }
36285         
36286         if(this.bgimage.length){
36287             cls += ' roo-brick-image';
36288         }
36289         
36290         if(!this.html.length && !this.bgimage.length){
36291             cls += ' roo-brick-center-title';
36292         }
36293         
36294         if(!this.html.length && this.bgimage.length){
36295             cls += ' roo-brick-bottom-title';
36296         }
36297         
36298         if(this.cls){
36299             cls += ' ' + this.cls;
36300         }
36301         
36302         var cfg = {
36303             tag: (this.href.length) ? 'a' : 'div',
36304             cls: cls,
36305             cn: [
36306                 {
36307                     tag: 'div',
36308                     cls: 'roo-brick-paragraph',
36309                     cn: []
36310                 }
36311             ]
36312         };
36313         
36314         if(this.href.length){
36315             cfg.href = this.href;
36316         }
36317         
36318         var cn = cfg.cn[0].cn;
36319         
36320         if(this.title.length){
36321             cn.push({
36322                 tag: 'h4',
36323                 cls: 'roo-brick-title',
36324                 html: this.title
36325             });
36326         }
36327         
36328         if(this.html.length){
36329             cn.push({
36330                 tag: 'p',
36331                 cls: 'roo-brick-text',
36332                 html: this.html
36333             });
36334         } else {
36335             cn.cls += ' hide';
36336         }
36337         
36338         if(this.bgimage.length){
36339             cfg.cn.push({
36340                 tag: 'img',
36341                 cls: 'roo-brick-image-view',
36342                 src: this.bgimage
36343             });
36344         }
36345         
36346         return cfg;
36347     },
36348     
36349     initEvents: function() 
36350     {
36351         if(this.title.length || this.html.length){
36352             this.el.on('mouseenter'  ,this.enter, this);
36353             this.el.on('mouseleave', this.leave, this);
36354         }
36355         
36356         Roo.EventManager.onWindowResize(this.resize, this); 
36357         
36358         if(this.bgimage.length){
36359             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36360             this.imageEl.on('load', this.onImageLoad, this);
36361             return;
36362         }
36363         
36364         this.resize();
36365     },
36366     
36367     onImageLoad : function()
36368     {
36369         this.resize();
36370     },
36371     
36372     resize : function()
36373     {
36374         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36375         
36376         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36377         
36378         if(this.bgimage.length){
36379             var image = this.el.select('.roo-brick-image-view', true).first();
36380             
36381             image.setWidth(paragraph.getWidth());
36382             
36383             if(this.square){
36384                 image.setHeight(paragraph.getWidth());
36385             }
36386             
36387             this.el.setHeight(image.getHeight());
36388             paragraph.setHeight(image.getHeight());
36389             
36390         }
36391         
36392     },
36393     
36394     enter: function(e, el)
36395     {
36396         e.preventDefault();
36397         
36398         if(this.bgimage.length){
36399             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36400             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36401         }
36402     },
36403     
36404     leave: function(e, el)
36405     {
36406         e.preventDefault();
36407         
36408         if(this.bgimage.length){
36409             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36410             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36411         }
36412     }
36413     
36414 });
36415
36416  
36417
36418  /*
36419  * - LGPL
36420  *
36421  * Number field 
36422  */
36423
36424 /**
36425  * @class Roo.bootstrap.NumberField
36426  * @extends Roo.bootstrap.Input
36427  * Bootstrap NumberField class
36428  * 
36429  * 
36430  * 
36431  * 
36432  * @constructor
36433  * Create a new NumberField
36434  * @param {Object} config The config object
36435  */
36436
36437 Roo.bootstrap.NumberField = function(config){
36438     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36439 };
36440
36441 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36442     
36443     /**
36444      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36445      */
36446     allowDecimals : true,
36447     /**
36448      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36449      */
36450     decimalSeparator : ".",
36451     /**
36452      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36453      */
36454     decimalPrecision : 2,
36455     /**
36456      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36457      */
36458     allowNegative : true,
36459     
36460     /**
36461      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36462      */
36463     allowZero: true,
36464     /**
36465      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36466      */
36467     minValue : Number.NEGATIVE_INFINITY,
36468     /**
36469      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36470      */
36471     maxValue : Number.MAX_VALUE,
36472     /**
36473      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36474      */
36475     minText : "The minimum value for this field is {0}",
36476     /**
36477      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36478      */
36479     maxText : "The maximum value for this field is {0}",
36480     /**
36481      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36482      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36483      */
36484     nanText : "{0} is not a valid number",
36485     /**
36486      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36487      */
36488     thousandsDelimiter : false,
36489     /**
36490      * @cfg {String} valueAlign alignment of value
36491      */
36492     valueAlign : "left",
36493
36494     getAutoCreate : function()
36495     {
36496         var hiddenInput = {
36497             tag: 'input',
36498             type: 'hidden',
36499             id: Roo.id(),
36500             cls: 'hidden-number-input'
36501         };
36502         
36503         if (this.name) {
36504             hiddenInput.name = this.name;
36505         }
36506         
36507         this.name = '';
36508         
36509         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36510         
36511         this.name = hiddenInput.name;
36512         
36513         if(cfg.cn.length > 0) {
36514             cfg.cn.push(hiddenInput);
36515         }
36516         
36517         return cfg;
36518     },
36519
36520     // private
36521     initEvents : function()
36522     {   
36523         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36524         
36525         var allowed = "0123456789";
36526         
36527         if(this.allowDecimals){
36528             allowed += this.decimalSeparator;
36529         }
36530         
36531         if(this.allowNegative){
36532             allowed += "-";
36533         }
36534         
36535         if(this.thousandsDelimiter) {
36536             allowed += ",";
36537         }
36538         
36539         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36540         
36541         var keyPress = function(e){
36542             
36543             var k = e.getKey();
36544             
36545             var c = e.getCharCode();
36546             
36547             if(
36548                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36549                     allowed.indexOf(String.fromCharCode(c)) === -1
36550             ){
36551                 e.stopEvent();
36552                 return;
36553             }
36554             
36555             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36556                 return;
36557             }
36558             
36559             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36560                 e.stopEvent();
36561             }
36562         };
36563         
36564         this.el.on("keypress", keyPress, this);
36565     },
36566     
36567     validateValue : function(value)
36568     {
36569         
36570         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36571             return false;
36572         }
36573         
36574         var num = this.parseValue(value);
36575         
36576         if(isNaN(num)){
36577             this.markInvalid(String.format(this.nanText, value));
36578             return false;
36579         }
36580         
36581         if(num < this.minValue){
36582             this.markInvalid(String.format(this.minText, this.minValue));
36583             return false;
36584         }
36585         
36586         if(num > this.maxValue){
36587             this.markInvalid(String.format(this.maxText, this.maxValue));
36588             return false;
36589         }
36590         
36591         return true;
36592     },
36593
36594     getValue : function()
36595     {
36596         var v = this.hiddenEl().getValue();
36597         
36598         return this.fixPrecision(this.parseValue(v));
36599     },
36600
36601     parseValue : function(value)
36602     {
36603         if(this.thousandsDelimiter) {
36604             value += "";
36605             r = new RegExp(",", "g");
36606             value = value.replace(r, "");
36607         }
36608         
36609         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36610         return isNaN(value) ? '' : value;
36611     },
36612
36613     fixPrecision : function(value)
36614     {
36615         if(this.thousandsDelimiter) {
36616             value += "";
36617             r = new RegExp(",", "g");
36618             value = value.replace(r, "");
36619         }
36620         
36621         var nan = isNaN(value);
36622         
36623         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36624             return nan ? '' : value;
36625         }
36626         return parseFloat(value).toFixed(this.decimalPrecision);
36627     },
36628
36629     setValue : function(v)
36630     {
36631         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36632         
36633         this.value = v;
36634         
36635         if(this.rendered){
36636             
36637             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36638             
36639             this.inputEl().dom.value = (v == '') ? '' :
36640                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36641             
36642             if(!this.allowZero && v === '0') {
36643                 this.hiddenEl().dom.value = '';
36644                 this.inputEl().dom.value = '';
36645             }
36646             
36647             this.validate();
36648         }
36649     },
36650
36651     decimalPrecisionFcn : function(v)
36652     {
36653         return Math.floor(v);
36654     },
36655
36656     beforeBlur : function()
36657     {
36658         var v = this.parseValue(this.getRawValue());
36659         
36660         if(v || v === 0 || v === ''){
36661             this.setValue(v);
36662         }
36663     },
36664     
36665     hiddenEl : function()
36666     {
36667         return this.el.select('input.hidden-number-input',true).first();
36668     }
36669     
36670 });
36671
36672  
36673
36674 /*
36675 * Licence: LGPL
36676 */
36677
36678 /**
36679  * @class Roo.bootstrap.DocumentSlider
36680  * @extends Roo.bootstrap.Component
36681  * Bootstrap DocumentSlider class
36682  * 
36683  * @constructor
36684  * Create a new DocumentViewer
36685  * @param {Object} config The config object
36686  */
36687
36688 Roo.bootstrap.DocumentSlider = function(config){
36689     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36690     
36691     this.files = [];
36692     
36693     this.addEvents({
36694         /**
36695          * @event initial
36696          * Fire after initEvent
36697          * @param {Roo.bootstrap.DocumentSlider} this
36698          */
36699         "initial" : true,
36700         /**
36701          * @event update
36702          * Fire after update
36703          * @param {Roo.bootstrap.DocumentSlider} this
36704          */
36705         "update" : true,
36706         /**
36707          * @event click
36708          * Fire after click
36709          * @param {Roo.bootstrap.DocumentSlider} this
36710          */
36711         "click" : true
36712     });
36713 };
36714
36715 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36716     
36717     files : false,
36718     
36719     indicator : 0,
36720     
36721     getAutoCreate : function()
36722     {
36723         var cfg = {
36724             tag : 'div',
36725             cls : 'roo-document-slider',
36726             cn : [
36727                 {
36728                     tag : 'div',
36729                     cls : 'roo-document-slider-header',
36730                     cn : [
36731                         {
36732                             tag : 'div',
36733                             cls : 'roo-document-slider-header-title'
36734                         }
36735                     ]
36736                 },
36737                 {
36738                     tag : 'div',
36739                     cls : 'roo-document-slider-body',
36740                     cn : [
36741                         {
36742                             tag : 'div',
36743                             cls : 'roo-document-slider-prev',
36744                             cn : [
36745                                 {
36746                                     tag : 'i',
36747                                     cls : 'fa fa-chevron-left'
36748                                 }
36749                             ]
36750                         },
36751                         {
36752                             tag : 'div',
36753                             cls : 'roo-document-slider-thumb',
36754                             cn : [
36755                                 {
36756                                     tag : 'img',
36757                                     cls : 'roo-document-slider-image'
36758                                 }
36759                             ]
36760                         },
36761                         {
36762                             tag : 'div',
36763                             cls : 'roo-document-slider-next',
36764                             cn : [
36765                                 {
36766                                     tag : 'i',
36767                                     cls : 'fa fa-chevron-right'
36768                                 }
36769                             ]
36770                         }
36771                     ]
36772                 }
36773             ]
36774         };
36775         
36776         return cfg;
36777     },
36778     
36779     initEvents : function()
36780     {
36781         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36782         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36783         
36784         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36785         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36786         
36787         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36788         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36789         
36790         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36791         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36792         
36793         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36794         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36795         
36796         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36797         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36798         
36799         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36800         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36801         
36802         this.thumbEl.on('click', this.onClick, this);
36803         
36804         this.prevIndicator.on('click', this.prev, this);
36805         
36806         this.nextIndicator.on('click', this.next, this);
36807         
36808     },
36809     
36810     initial : function()
36811     {
36812         if(this.files.length){
36813             this.indicator = 1;
36814             this.update()
36815         }
36816         
36817         this.fireEvent('initial', this);
36818     },
36819     
36820     update : function()
36821     {
36822         this.imageEl.attr('src', this.files[this.indicator - 1]);
36823         
36824         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36825         
36826         this.prevIndicator.show();
36827         
36828         if(this.indicator == 1){
36829             this.prevIndicator.hide();
36830         }
36831         
36832         this.nextIndicator.show();
36833         
36834         if(this.indicator == this.files.length){
36835             this.nextIndicator.hide();
36836         }
36837         
36838         this.thumbEl.scrollTo('top');
36839         
36840         this.fireEvent('update', this);
36841     },
36842     
36843     onClick : function(e)
36844     {
36845         e.preventDefault();
36846         
36847         this.fireEvent('click', this);
36848     },
36849     
36850     prev : function(e)
36851     {
36852         e.preventDefault();
36853         
36854         this.indicator = Math.max(1, this.indicator - 1);
36855         
36856         this.update();
36857     },
36858     
36859     next : function(e)
36860     {
36861         e.preventDefault();
36862         
36863         this.indicator = Math.min(this.files.length, this.indicator + 1);
36864         
36865         this.update();
36866     }
36867 });
36868 /*
36869  * - LGPL
36870  *
36871  * RadioSet
36872  *
36873  *
36874  */
36875
36876 /**
36877  * @class Roo.bootstrap.RadioSet
36878  * @extends Roo.bootstrap.Input
36879  * Bootstrap RadioSet class
36880  * @cfg {String} indicatorpos (left|right) default left
36881  * @cfg {Boolean} inline (true|false) inline the element (default true)
36882  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36883  * @constructor
36884  * Create a new RadioSet
36885  * @param {Object} config The config object
36886  */
36887
36888 Roo.bootstrap.RadioSet = function(config){
36889     
36890     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36891     
36892     this.radioes = [];
36893     
36894     Roo.bootstrap.RadioSet.register(this);
36895     
36896     this.addEvents({
36897         /**
36898         * @event check
36899         * Fires when the element is checked or unchecked.
36900         * @param {Roo.bootstrap.RadioSet} this This radio
36901         * @param {Roo.bootstrap.Radio} item The checked item
36902         */
36903        check : true,
36904        /**
36905         * @event click
36906         * Fires when the element is click.
36907         * @param {Roo.bootstrap.RadioSet} this This radio set
36908         * @param {Roo.bootstrap.Radio} item The checked item
36909         * @param {Roo.EventObject} e The event object
36910         */
36911        click : true
36912     });
36913     
36914 };
36915
36916 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36917
36918     radioes : false,
36919     
36920     inline : true,
36921     
36922     weight : '',
36923     
36924     indicatorpos : 'left',
36925     
36926     getAutoCreate : function()
36927     {
36928         var label = {
36929             tag : 'label',
36930             cls : 'roo-radio-set-label',
36931             cn : [
36932                 {
36933                     tag : 'span',
36934                     html : this.fieldLabel
36935                 }
36936             ]
36937         };
36938         if (Roo.bootstrap.version == 3) {
36939             
36940             
36941             if(this.indicatorpos == 'left'){
36942                 label.cn.unshift({
36943                     tag : 'i',
36944                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36945                     tooltip : 'This field is required'
36946                 });
36947             } else {
36948                 label.cn.push({
36949                     tag : 'i',
36950                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36951                     tooltip : 'This field is required'
36952                 });
36953             }
36954         }
36955         var items = {
36956             tag : 'div',
36957             cls : 'roo-radio-set-items'
36958         };
36959         
36960         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36961         
36962         if (align === 'left' && this.fieldLabel.length) {
36963             
36964             items = {
36965                 cls : "roo-radio-set-right", 
36966                 cn: [
36967                     items
36968                 ]
36969             };
36970             
36971             if(this.labelWidth > 12){
36972                 label.style = "width: " + this.labelWidth + 'px';
36973             }
36974             
36975             if(this.labelWidth < 13 && this.labelmd == 0){
36976                 this.labelmd = this.labelWidth;
36977             }
36978             
36979             if(this.labellg > 0){
36980                 label.cls += ' col-lg-' + this.labellg;
36981                 items.cls += ' col-lg-' + (12 - this.labellg);
36982             }
36983             
36984             if(this.labelmd > 0){
36985                 label.cls += ' col-md-' + this.labelmd;
36986                 items.cls += ' col-md-' + (12 - this.labelmd);
36987             }
36988             
36989             if(this.labelsm > 0){
36990                 label.cls += ' col-sm-' + this.labelsm;
36991                 items.cls += ' col-sm-' + (12 - this.labelsm);
36992             }
36993             
36994             if(this.labelxs > 0){
36995                 label.cls += ' col-xs-' + this.labelxs;
36996                 items.cls += ' col-xs-' + (12 - this.labelxs);
36997             }
36998         }
36999         
37000         var cfg = {
37001             tag : 'div',
37002             cls : 'roo-radio-set',
37003             cn : [
37004                 {
37005                     tag : 'input',
37006                     cls : 'roo-radio-set-input',
37007                     type : 'hidden',
37008                     name : this.name,
37009                     value : this.value ? this.value :  ''
37010                 },
37011                 label,
37012                 items
37013             ]
37014         };
37015         
37016         if(this.weight.length){
37017             cfg.cls += ' roo-radio-' + this.weight;
37018         }
37019         
37020         if(this.inline) {
37021             cfg.cls += ' roo-radio-set-inline';
37022         }
37023         
37024         var settings=this;
37025         ['xs','sm','md','lg'].map(function(size){
37026             if (settings[size]) {
37027                 cfg.cls += ' col-' + size + '-' + settings[size];
37028             }
37029         });
37030         
37031         return cfg;
37032         
37033     },
37034
37035     initEvents : function()
37036     {
37037         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37038         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37039         
37040         if(!this.fieldLabel.length){
37041             this.labelEl.hide();
37042         }
37043         
37044         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37045         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37046         
37047         this.indicator = this.indicatorEl();
37048         
37049         if(this.indicator){
37050             this.indicator.addClass('invisible');
37051         }
37052         
37053         this.originalValue = this.getValue();
37054         
37055     },
37056     
37057     inputEl: function ()
37058     {
37059         return this.el.select('.roo-radio-set-input', true).first();
37060     },
37061     
37062     getChildContainer : function()
37063     {
37064         return this.itemsEl;
37065     },
37066     
37067     register : function(item)
37068     {
37069         this.radioes.push(item);
37070         
37071     },
37072     
37073     validate : function()
37074     {   
37075         if(this.getVisibilityEl().hasClass('hidden')){
37076             return true;
37077         }
37078         
37079         var valid = false;
37080         
37081         Roo.each(this.radioes, function(i){
37082             if(!i.checked){
37083                 return;
37084             }
37085             
37086             valid = true;
37087             return false;
37088         });
37089         
37090         if(this.allowBlank) {
37091             return true;
37092         }
37093         
37094         if(this.disabled || valid){
37095             this.markValid();
37096             return true;
37097         }
37098         
37099         this.markInvalid();
37100         return false;
37101         
37102     },
37103     
37104     markValid : function()
37105     {
37106         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37107             this.indicatorEl().removeClass('visible');
37108             this.indicatorEl().addClass('invisible');
37109         }
37110         
37111         
37112         if (Roo.bootstrap.version == 3) {
37113             this.el.removeClass([this.invalidClass, this.validClass]);
37114             this.el.addClass(this.validClass);
37115         } else {
37116             this.el.removeClass(['is-invalid','is-valid']);
37117             this.el.addClass(['is-valid']);
37118         }
37119         this.fireEvent('valid', this);
37120     },
37121     
37122     markInvalid : function(msg)
37123     {
37124         if(this.allowBlank || this.disabled){
37125             return;
37126         }
37127         
37128         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37129             this.indicatorEl().removeClass('invisible');
37130             this.indicatorEl().addClass('visible');
37131         }
37132         if (Roo.bootstrap.version == 3) {
37133             this.el.removeClass([this.invalidClass, this.validClass]);
37134             this.el.addClass(this.invalidClass);
37135         } else {
37136             this.el.removeClass(['is-invalid','is-valid']);
37137             this.el.addClass(['is-invalid']);
37138         }
37139         
37140         this.fireEvent('invalid', this, msg);
37141         
37142     },
37143     
37144     setValue : function(v, suppressEvent)
37145     {   
37146         if(this.value === v){
37147             return;
37148         }
37149         
37150         this.value = v;
37151         
37152         if(this.rendered){
37153             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37154         }
37155         
37156         Roo.each(this.radioes, function(i){
37157             i.checked = false;
37158             i.el.removeClass('checked');
37159         });
37160         
37161         Roo.each(this.radioes, function(i){
37162             
37163             if(i.value === v || i.value.toString() === v.toString()){
37164                 i.checked = true;
37165                 i.el.addClass('checked');
37166                 
37167                 if(suppressEvent !== true){
37168                     this.fireEvent('check', this, i);
37169                 }
37170                 
37171                 return false;
37172             }
37173             
37174         }, this);
37175         
37176         this.validate();
37177     },
37178     
37179     clearInvalid : function(){
37180         
37181         if(!this.el || this.preventMark){
37182             return;
37183         }
37184         
37185         this.el.removeClass([this.invalidClass]);
37186         
37187         this.fireEvent('valid', this);
37188     }
37189     
37190 });
37191
37192 Roo.apply(Roo.bootstrap.RadioSet, {
37193     
37194     groups: {},
37195     
37196     register : function(set)
37197     {
37198         this.groups[set.name] = set;
37199     },
37200     
37201     get: function(name) 
37202     {
37203         if (typeof(this.groups[name]) == 'undefined') {
37204             return false;
37205         }
37206         
37207         return this.groups[name] ;
37208     }
37209     
37210 });
37211 /*
37212  * Based on:
37213  * Ext JS Library 1.1.1
37214  * Copyright(c) 2006-2007, Ext JS, LLC.
37215  *
37216  * Originally Released Under LGPL - original licence link has changed is not relivant.
37217  *
37218  * Fork - LGPL
37219  * <script type="text/javascript">
37220  */
37221
37222
37223 /**
37224  * @class Roo.bootstrap.SplitBar
37225  * @extends Roo.util.Observable
37226  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37227  * <br><br>
37228  * Usage:
37229  * <pre><code>
37230 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37231                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37232 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37233 split.minSize = 100;
37234 split.maxSize = 600;
37235 split.animate = true;
37236 split.on('moved', splitterMoved);
37237 </code></pre>
37238  * @constructor
37239  * Create a new SplitBar
37240  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37241  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37242  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37243  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37244                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37245                         position of the SplitBar).
37246  */
37247 Roo.bootstrap.SplitBar = function(cfg){
37248     
37249     /** @private */
37250     
37251     //{
37252     //  dragElement : elm
37253     //  resizingElement: el,
37254         // optional..
37255     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37256     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37257         // existingProxy ???
37258     //}
37259     
37260     this.el = Roo.get(cfg.dragElement, true);
37261     this.el.dom.unselectable = "on";
37262     /** @private */
37263     this.resizingEl = Roo.get(cfg.resizingElement, true);
37264
37265     /**
37266      * @private
37267      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37268      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37269      * @type Number
37270      */
37271     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37272     
37273     /**
37274      * The minimum size of the resizing element. (Defaults to 0)
37275      * @type Number
37276      */
37277     this.minSize = 0;
37278     
37279     /**
37280      * The maximum size of the resizing element. (Defaults to 2000)
37281      * @type Number
37282      */
37283     this.maxSize = 2000;
37284     
37285     /**
37286      * Whether to animate the transition to the new size
37287      * @type Boolean
37288      */
37289     this.animate = false;
37290     
37291     /**
37292      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37293      * @type Boolean
37294      */
37295     this.useShim = false;
37296     
37297     /** @private */
37298     this.shim = null;
37299     
37300     if(!cfg.existingProxy){
37301         /** @private */
37302         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37303     }else{
37304         this.proxy = Roo.get(cfg.existingProxy).dom;
37305     }
37306     /** @private */
37307     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37308     
37309     /** @private */
37310     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37311     
37312     /** @private */
37313     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37314     
37315     /** @private */
37316     this.dragSpecs = {};
37317     
37318     /**
37319      * @private The adapter to use to positon and resize elements
37320      */
37321     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37322     this.adapter.init(this);
37323     
37324     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37325         /** @private */
37326         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37327         this.el.addClass("roo-splitbar-h");
37328     }else{
37329         /** @private */
37330         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37331         this.el.addClass("roo-splitbar-v");
37332     }
37333     
37334     this.addEvents({
37335         /**
37336          * @event resize
37337          * Fires when the splitter is moved (alias for {@link #event-moved})
37338          * @param {Roo.bootstrap.SplitBar} this
37339          * @param {Number} newSize the new width or height
37340          */
37341         "resize" : true,
37342         /**
37343          * @event moved
37344          * Fires when the splitter is moved
37345          * @param {Roo.bootstrap.SplitBar} this
37346          * @param {Number} newSize the new width or height
37347          */
37348         "moved" : true,
37349         /**
37350          * @event beforeresize
37351          * Fires before the splitter is dragged
37352          * @param {Roo.bootstrap.SplitBar} this
37353          */
37354         "beforeresize" : true,
37355
37356         "beforeapply" : true
37357     });
37358
37359     Roo.util.Observable.call(this);
37360 };
37361
37362 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37363     onStartProxyDrag : function(x, y){
37364         this.fireEvent("beforeresize", this);
37365         if(!this.overlay){
37366             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37367             o.unselectable();
37368             o.enableDisplayMode("block");
37369             // all splitbars share the same overlay
37370             Roo.bootstrap.SplitBar.prototype.overlay = o;
37371         }
37372         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37373         this.overlay.show();
37374         Roo.get(this.proxy).setDisplayed("block");
37375         var size = this.adapter.getElementSize(this);
37376         this.activeMinSize = this.getMinimumSize();;
37377         this.activeMaxSize = this.getMaximumSize();;
37378         var c1 = size - this.activeMinSize;
37379         var c2 = Math.max(this.activeMaxSize - size, 0);
37380         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37381             this.dd.resetConstraints();
37382             this.dd.setXConstraint(
37383                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37384                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37385             );
37386             this.dd.setYConstraint(0, 0);
37387         }else{
37388             this.dd.resetConstraints();
37389             this.dd.setXConstraint(0, 0);
37390             this.dd.setYConstraint(
37391                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37392                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37393             );
37394          }
37395         this.dragSpecs.startSize = size;
37396         this.dragSpecs.startPoint = [x, y];
37397         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37398     },
37399     
37400     /** 
37401      * @private Called after the drag operation by the DDProxy
37402      */
37403     onEndProxyDrag : function(e){
37404         Roo.get(this.proxy).setDisplayed(false);
37405         var endPoint = Roo.lib.Event.getXY(e);
37406         if(this.overlay){
37407             this.overlay.hide();
37408         }
37409         var newSize;
37410         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37411             newSize = this.dragSpecs.startSize + 
37412                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37413                     endPoint[0] - this.dragSpecs.startPoint[0] :
37414                     this.dragSpecs.startPoint[0] - endPoint[0]
37415                 );
37416         }else{
37417             newSize = this.dragSpecs.startSize + 
37418                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37419                     endPoint[1] - this.dragSpecs.startPoint[1] :
37420                     this.dragSpecs.startPoint[1] - endPoint[1]
37421                 );
37422         }
37423         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37424         if(newSize != this.dragSpecs.startSize){
37425             if(this.fireEvent('beforeapply', this, newSize) !== false){
37426                 this.adapter.setElementSize(this, newSize);
37427                 this.fireEvent("moved", this, newSize);
37428                 this.fireEvent("resize", this, newSize);
37429             }
37430         }
37431     },
37432     
37433     /**
37434      * Get the adapter this SplitBar uses
37435      * @return The adapter object
37436      */
37437     getAdapter : function(){
37438         return this.adapter;
37439     },
37440     
37441     /**
37442      * Set the adapter this SplitBar uses
37443      * @param {Object} adapter A SplitBar adapter object
37444      */
37445     setAdapter : function(adapter){
37446         this.adapter = adapter;
37447         this.adapter.init(this);
37448     },
37449     
37450     /**
37451      * Gets the minimum size for the resizing element
37452      * @return {Number} The minimum size
37453      */
37454     getMinimumSize : function(){
37455         return this.minSize;
37456     },
37457     
37458     /**
37459      * Sets the minimum size for the resizing element
37460      * @param {Number} minSize The minimum size
37461      */
37462     setMinimumSize : function(minSize){
37463         this.minSize = minSize;
37464     },
37465     
37466     /**
37467      * Gets the maximum size for the resizing element
37468      * @return {Number} The maximum size
37469      */
37470     getMaximumSize : function(){
37471         return this.maxSize;
37472     },
37473     
37474     /**
37475      * Sets the maximum size for the resizing element
37476      * @param {Number} maxSize The maximum size
37477      */
37478     setMaximumSize : function(maxSize){
37479         this.maxSize = maxSize;
37480     },
37481     
37482     /**
37483      * Sets the initialize size for the resizing element
37484      * @param {Number} size The initial size
37485      */
37486     setCurrentSize : function(size){
37487         var oldAnimate = this.animate;
37488         this.animate = false;
37489         this.adapter.setElementSize(this, size);
37490         this.animate = oldAnimate;
37491     },
37492     
37493     /**
37494      * Destroy this splitbar. 
37495      * @param {Boolean} removeEl True to remove the element
37496      */
37497     destroy : function(removeEl){
37498         if(this.shim){
37499             this.shim.remove();
37500         }
37501         this.dd.unreg();
37502         this.proxy.parentNode.removeChild(this.proxy);
37503         if(removeEl){
37504             this.el.remove();
37505         }
37506     }
37507 });
37508
37509 /**
37510  * @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.
37511  */
37512 Roo.bootstrap.SplitBar.createProxy = function(dir){
37513     var proxy = new Roo.Element(document.createElement("div"));
37514     proxy.unselectable();
37515     var cls = 'roo-splitbar-proxy';
37516     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37517     document.body.appendChild(proxy.dom);
37518     return proxy.dom;
37519 };
37520
37521 /** 
37522  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37523  * Default Adapter. It assumes the splitter and resizing element are not positioned
37524  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37525  */
37526 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37527 };
37528
37529 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37530     // do nothing for now
37531     init : function(s){
37532     
37533     },
37534     /**
37535      * Called before drag operations to get the current size of the resizing element. 
37536      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37537      */
37538      getElementSize : function(s){
37539         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540             return s.resizingEl.getWidth();
37541         }else{
37542             return s.resizingEl.getHeight();
37543         }
37544     },
37545     
37546     /**
37547      * Called after drag operations to set the size of the resizing element.
37548      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37549      * @param {Number} newSize The new size to set
37550      * @param {Function} onComplete A function to be invoked when resizing is complete
37551      */
37552     setElementSize : function(s, newSize, onComplete){
37553         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37554             if(!s.animate){
37555                 s.resizingEl.setWidth(newSize);
37556                 if(onComplete){
37557                     onComplete(s, newSize);
37558                 }
37559             }else{
37560                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37561             }
37562         }else{
37563             
37564             if(!s.animate){
37565                 s.resizingEl.setHeight(newSize);
37566                 if(onComplete){
37567                     onComplete(s, newSize);
37568                 }
37569             }else{
37570                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37571             }
37572         }
37573     }
37574 };
37575
37576 /** 
37577  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37578  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37579  * Adapter that  moves the splitter element to align with the resized sizing element. 
37580  * Used with an absolute positioned SplitBar.
37581  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37582  * document.body, make sure you assign an id to the body element.
37583  */
37584 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37585     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37586     this.container = Roo.get(container);
37587 };
37588
37589 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37590     init : function(s){
37591         this.basic.init(s);
37592     },
37593     
37594     getElementSize : function(s){
37595         return this.basic.getElementSize(s);
37596     },
37597     
37598     setElementSize : function(s, newSize, onComplete){
37599         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37600     },
37601     
37602     moveSplitter : function(s){
37603         var yes = Roo.bootstrap.SplitBar;
37604         switch(s.placement){
37605             case yes.LEFT:
37606                 s.el.setX(s.resizingEl.getRight());
37607                 break;
37608             case yes.RIGHT:
37609                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37610                 break;
37611             case yes.TOP:
37612                 s.el.setY(s.resizingEl.getBottom());
37613                 break;
37614             case yes.BOTTOM:
37615                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37616                 break;
37617         }
37618     }
37619 };
37620
37621 /**
37622  * Orientation constant - Create a vertical SplitBar
37623  * @static
37624  * @type Number
37625  */
37626 Roo.bootstrap.SplitBar.VERTICAL = 1;
37627
37628 /**
37629  * Orientation constant - Create a horizontal SplitBar
37630  * @static
37631  * @type Number
37632  */
37633 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37634
37635 /**
37636  * Placement constant - The resizing element is to the left of the splitter element
37637  * @static
37638  * @type Number
37639  */
37640 Roo.bootstrap.SplitBar.LEFT = 1;
37641
37642 /**
37643  * Placement constant - The resizing element is to the right of the splitter element
37644  * @static
37645  * @type Number
37646  */
37647 Roo.bootstrap.SplitBar.RIGHT = 2;
37648
37649 /**
37650  * Placement constant - The resizing element is positioned above the splitter element
37651  * @static
37652  * @type Number
37653  */
37654 Roo.bootstrap.SplitBar.TOP = 3;
37655
37656 /**
37657  * Placement constant - The resizing element is positioned under splitter element
37658  * @static
37659  * @type Number
37660  */
37661 Roo.bootstrap.SplitBar.BOTTOM = 4;
37662 Roo.namespace("Roo.bootstrap.layout");/*
37663  * Based on:
37664  * Ext JS Library 1.1.1
37665  * Copyright(c) 2006-2007, Ext JS, LLC.
37666  *
37667  * Originally Released Under LGPL - original licence link has changed is not relivant.
37668  *
37669  * Fork - LGPL
37670  * <script type="text/javascript">
37671  */
37672
37673 /**
37674  * @class Roo.bootstrap.layout.Manager
37675  * @extends Roo.bootstrap.Component
37676  * Base class for layout managers.
37677  */
37678 Roo.bootstrap.layout.Manager = function(config)
37679 {
37680     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37681
37682
37683
37684
37685
37686     /** false to disable window resize monitoring @type Boolean */
37687     this.monitorWindowResize = true;
37688     this.regions = {};
37689     this.addEvents({
37690         /**
37691          * @event layout
37692          * Fires when a layout is performed.
37693          * @param {Roo.LayoutManager} this
37694          */
37695         "layout" : true,
37696         /**
37697          * @event regionresized
37698          * Fires when the user resizes a region.
37699          * @param {Roo.LayoutRegion} region The resized region
37700          * @param {Number} newSize The new size (width for east/west, height for north/south)
37701          */
37702         "regionresized" : true,
37703         /**
37704          * @event regioncollapsed
37705          * Fires when a region is collapsed.
37706          * @param {Roo.LayoutRegion} region The collapsed region
37707          */
37708         "regioncollapsed" : true,
37709         /**
37710          * @event regionexpanded
37711          * Fires when a region is expanded.
37712          * @param {Roo.LayoutRegion} region The expanded region
37713          */
37714         "regionexpanded" : true
37715     });
37716     this.updating = false;
37717
37718     if (config.el) {
37719         this.el = Roo.get(config.el);
37720         this.initEvents();
37721     }
37722
37723 };
37724
37725 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37726
37727
37728     regions : null,
37729
37730     monitorWindowResize : true,
37731
37732
37733     updating : false,
37734
37735
37736     onRender : function(ct, position)
37737     {
37738         if(!this.el){
37739             this.el = Roo.get(ct);
37740             this.initEvents();
37741         }
37742         //this.fireEvent('render',this);
37743     },
37744
37745
37746     initEvents: function()
37747     {
37748
37749
37750         // ie scrollbar fix
37751         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37752             document.body.scroll = "no";
37753         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37754             this.el.position('relative');
37755         }
37756         this.id = this.el.id;
37757         this.el.addClass("roo-layout-container");
37758         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37759         if(this.el.dom != document.body ) {
37760             this.el.on('resize', this.layout,this);
37761             this.el.on('show', this.layout,this);
37762         }
37763
37764     },
37765
37766     /**
37767      * Returns true if this layout is currently being updated
37768      * @return {Boolean}
37769      */
37770     isUpdating : function(){
37771         return this.updating;
37772     },
37773
37774     /**
37775      * Suspend the LayoutManager from doing auto-layouts while
37776      * making multiple add or remove calls
37777      */
37778     beginUpdate : function(){
37779         this.updating = true;
37780     },
37781
37782     /**
37783      * Restore auto-layouts and optionally disable the manager from performing a layout
37784      * @param {Boolean} noLayout true to disable a layout update
37785      */
37786     endUpdate : function(noLayout){
37787         this.updating = false;
37788         if(!noLayout){
37789             this.layout();
37790         }
37791     },
37792
37793     layout: function(){
37794         // abstract...
37795     },
37796
37797     onRegionResized : function(region, newSize){
37798         this.fireEvent("regionresized", region, newSize);
37799         this.layout();
37800     },
37801
37802     onRegionCollapsed : function(region){
37803         this.fireEvent("regioncollapsed", region);
37804     },
37805
37806     onRegionExpanded : function(region){
37807         this.fireEvent("regionexpanded", region);
37808     },
37809
37810     /**
37811      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37812      * performs box-model adjustments.
37813      * @return {Object} The size as an object {width: (the width), height: (the height)}
37814      */
37815     getViewSize : function()
37816     {
37817         var size;
37818         if(this.el.dom != document.body){
37819             size = this.el.getSize();
37820         }else{
37821             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37822         }
37823         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37824         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37825         return size;
37826     },
37827
37828     /**
37829      * Returns the Element this layout is bound to.
37830      * @return {Roo.Element}
37831      */
37832     getEl : function(){
37833         return this.el;
37834     },
37835
37836     /**
37837      * Returns the specified region.
37838      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37839      * @return {Roo.LayoutRegion}
37840      */
37841     getRegion : function(target){
37842         return this.regions[target.toLowerCase()];
37843     },
37844
37845     onWindowResize : function(){
37846         if(this.monitorWindowResize){
37847             this.layout();
37848         }
37849     }
37850 });
37851 /*
37852  * Based on:
37853  * Ext JS Library 1.1.1
37854  * Copyright(c) 2006-2007, Ext JS, LLC.
37855  *
37856  * Originally Released Under LGPL - original licence link has changed is not relivant.
37857  *
37858  * Fork - LGPL
37859  * <script type="text/javascript">
37860  */
37861 /**
37862  * @class Roo.bootstrap.layout.Border
37863  * @extends Roo.bootstrap.layout.Manager
37864  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37865  * please see: examples/bootstrap/nested.html<br><br>
37866  
37867 <b>The container the layout is rendered into can be either the body element or any other element.
37868 If it is not the body element, the container needs to either be an absolute positioned element,
37869 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37870 the container size if it is not the body element.</b>
37871
37872 * @constructor
37873 * Create a new Border
37874 * @param {Object} config Configuration options
37875  */
37876 Roo.bootstrap.layout.Border = function(config){
37877     config = config || {};
37878     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37879     
37880     
37881     
37882     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37883         if(config[region]){
37884             config[region].region = region;
37885             this.addRegion(config[region]);
37886         }
37887     },this);
37888     
37889 };
37890
37891 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37892
37893 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37894     
37895     parent : false, // this might point to a 'nest' or a ???
37896     
37897     /**
37898      * Creates and adds a new region if it doesn't already exist.
37899      * @param {String} target The target region key (north, south, east, west or center).
37900      * @param {Object} config The regions config object
37901      * @return {BorderLayoutRegion} The new region
37902      */
37903     addRegion : function(config)
37904     {
37905         if(!this.regions[config.region]){
37906             var r = this.factory(config);
37907             this.bindRegion(r);
37908         }
37909         return this.regions[config.region];
37910     },
37911
37912     // private (kinda)
37913     bindRegion : function(r){
37914         this.regions[r.config.region] = r;
37915         
37916         r.on("visibilitychange",    this.layout, this);
37917         r.on("paneladded",          this.layout, this);
37918         r.on("panelremoved",        this.layout, this);
37919         r.on("invalidated",         this.layout, this);
37920         r.on("resized",             this.onRegionResized, this);
37921         r.on("collapsed",           this.onRegionCollapsed, this);
37922         r.on("expanded",            this.onRegionExpanded, this);
37923     },
37924
37925     /**
37926      * Performs a layout update.
37927      */
37928     layout : function()
37929     {
37930         if(this.updating) {
37931             return;
37932         }
37933         
37934         // render all the rebions if they have not been done alreayd?
37935         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37936             if(this.regions[region] && !this.regions[region].bodyEl){
37937                 this.regions[region].onRender(this.el)
37938             }
37939         },this);
37940         
37941         var size = this.getViewSize();
37942         var w = size.width;
37943         var h = size.height;
37944         var centerW = w;
37945         var centerH = h;
37946         var centerY = 0;
37947         var centerX = 0;
37948         //var x = 0, y = 0;
37949
37950         var rs = this.regions;
37951         var north = rs["north"];
37952         var south = rs["south"]; 
37953         var west = rs["west"];
37954         var east = rs["east"];
37955         var center = rs["center"];
37956         //if(this.hideOnLayout){ // not supported anymore
37957             //c.el.setStyle("display", "none");
37958         //}
37959         if(north && north.isVisible()){
37960             var b = north.getBox();
37961             var m = north.getMargins();
37962             b.width = w - (m.left+m.right);
37963             b.x = m.left;
37964             b.y = m.top;
37965             centerY = b.height + b.y + m.bottom;
37966             centerH -= centerY;
37967             north.updateBox(this.safeBox(b));
37968         }
37969         if(south && south.isVisible()){
37970             var b = south.getBox();
37971             var m = south.getMargins();
37972             b.width = w - (m.left+m.right);
37973             b.x = m.left;
37974             var totalHeight = (b.height + m.top + m.bottom);
37975             b.y = h - totalHeight + m.top;
37976             centerH -= totalHeight;
37977             south.updateBox(this.safeBox(b));
37978         }
37979         if(west && west.isVisible()){
37980             var b = west.getBox();
37981             var m = west.getMargins();
37982             b.height = centerH - (m.top+m.bottom);
37983             b.x = m.left;
37984             b.y = centerY + m.top;
37985             var totalWidth = (b.width + m.left + m.right);
37986             centerX += totalWidth;
37987             centerW -= totalWidth;
37988             west.updateBox(this.safeBox(b));
37989         }
37990         if(east && east.isVisible()){
37991             var b = east.getBox();
37992             var m = east.getMargins();
37993             b.height = centerH - (m.top+m.bottom);
37994             var totalWidth = (b.width + m.left + m.right);
37995             b.x = w - totalWidth + m.left;
37996             b.y = centerY + m.top;
37997             centerW -= totalWidth;
37998             east.updateBox(this.safeBox(b));
37999         }
38000         if(center){
38001             var m = center.getMargins();
38002             var centerBox = {
38003                 x: centerX + m.left,
38004                 y: centerY + m.top,
38005                 width: centerW - (m.left+m.right),
38006                 height: centerH - (m.top+m.bottom)
38007             };
38008             //if(this.hideOnLayout){
38009                 //center.el.setStyle("display", "block");
38010             //}
38011             center.updateBox(this.safeBox(centerBox));
38012         }
38013         this.el.repaint();
38014         this.fireEvent("layout", this);
38015     },
38016
38017     // private
38018     safeBox : function(box){
38019         box.width = Math.max(0, box.width);
38020         box.height = Math.max(0, box.height);
38021         return box;
38022     },
38023
38024     /**
38025      * Adds a ContentPanel (or subclass) to this layout.
38026      * @param {String} target The target region key (north, south, east, west or center).
38027      * @param {Roo.ContentPanel} panel The panel to add
38028      * @return {Roo.ContentPanel} The added panel
38029      */
38030     add : function(target, panel){
38031          
38032         target = target.toLowerCase();
38033         return this.regions[target].add(panel);
38034     },
38035
38036     /**
38037      * Remove a ContentPanel (or subclass) to this layout.
38038      * @param {String} target The target region key (north, south, east, west or center).
38039      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38040      * @return {Roo.ContentPanel} The removed panel
38041      */
38042     remove : function(target, panel){
38043         target = target.toLowerCase();
38044         return this.regions[target].remove(panel);
38045     },
38046
38047     /**
38048      * Searches all regions for a panel with the specified id
38049      * @param {String} panelId
38050      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38051      */
38052     findPanel : function(panelId){
38053         var rs = this.regions;
38054         for(var target in rs){
38055             if(typeof rs[target] != "function"){
38056                 var p = rs[target].getPanel(panelId);
38057                 if(p){
38058                     return p;
38059                 }
38060             }
38061         }
38062         return null;
38063     },
38064
38065     /**
38066      * Searches all regions for a panel with the specified id and activates (shows) it.
38067      * @param {String/ContentPanel} panelId The panels id or the panel itself
38068      * @return {Roo.ContentPanel} The shown panel or null
38069      */
38070     showPanel : function(panelId) {
38071       var rs = this.regions;
38072       for(var target in rs){
38073          var r = rs[target];
38074          if(typeof r != "function"){
38075             if(r.hasPanel(panelId)){
38076                return r.showPanel(panelId);
38077             }
38078          }
38079       }
38080       return null;
38081    },
38082
38083    /**
38084      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38085      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38086      */
38087    /*
38088     restoreState : function(provider){
38089         if(!provider){
38090             provider = Roo.state.Manager;
38091         }
38092         var sm = new Roo.LayoutStateManager();
38093         sm.init(this, provider);
38094     },
38095 */
38096  
38097  
38098     /**
38099      * Adds a xtype elements to the layout.
38100      * <pre><code>
38101
38102 layout.addxtype({
38103        xtype : 'ContentPanel',
38104        region: 'west',
38105        items: [ .... ]
38106    }
38107 );
38108
38109 layout.addxtype({
38110         xtype : 'NestedLayoutPanel',
38111         region: 'west',
38112         layout: {
38113            center: { },
38114            west: { }   
38115         },
38116         items : [ ... list of content panels or nested layout panels.. ]
38117    }
38118 );
38119 </code></pre>
38120      * @param {Object} cfg Xtype definition of item to add.
38121      */
38122     addxtype : function(cfg)
38123     {
38124         // basically accepts a pannel...
38125         // can accept a layout region..!?!?
38126         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38127         
38128         
38129         // theory?  children can only be panels??
38130         
38131         //if (!cfg.xtype.match(/Panel$/)) {
38132         //    return false;
38133         //}
38134         var ret = false;
38135         
38136         if (typeof(cfg.region) == 'undefined') {
38137             Roo.log("Failed to add Panel, region was not set");
38138             Roo.log(cfg);
38139             return false;
38140         }
38141         var region = cfg.region;
38142         delete cfg.region;
38143         
38144           
38145         var xitems = [];
38146         if (cfg.items) {
38147             xitems = cfg.items;
38148             delete cfg.items;
38149         }
38150         var nb = false;
38151         
38152         if ( region == 'center') {
38153             Roo.log("Center: " + cfg.title);
38154         }
38155         
38156         
38157         switch(cfg.xtype) 
38158         {
38159             case 'Content':  // ContentPanel (el, cfg)
38160             case 'Scroll':  // ContentPanel (el, cfg)
38161             case 'View': 
38162                 cfg.autoCreate = cfg.autoCreate || true;
38163                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38164                 //} else {
38165                 //    var el = this.el.createChild();
38166                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38167                 //}
38168                 
38169                 this.add(region, ret);
38170                 break;
38171             
38172             /*
38173             case 'TreePanel': // our new panel!
38174                 cfg.el = this.el.createChild();
38175                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38176                 this.add(region, ret);
38177                 break;
38178             */
38179             
38180             case 'Nest': 
38181                 // create a new Layout (which is  a Border Layout...
38182                 
38183                 var clayout = cfg.layout;
38184                 clayout.el  = this.el.createChild();
38185                 clayout.items   = clayout.items  || [];
38186                 
38187                 delete cfg.layout;
38188                 
38189                 // replace this exitems with the clayout ones..
38190                 xitems = clayout.items;
38191                  
38192                 // force background off if it's in center...
38193                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38194                     cfg.background = false;
38195                 }
38196                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38197                 
38198                 
38199                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38200                 //console.log('adding nested layout panel '  + cfg.toSource());
38201                 this.add(region, ret);
38202                 nb = {}; /// find first...
38203                 break;
38204             
38205             case 'Grid':
38206                 
38207                 // needs grid and region
38208                 
38209                 //var el = this.getRegion(region).el.createChild();
38210                 /*
38211                  *var el = this.el.createChild();
38212                 // create the grid first...
38213                 cfg.grid.container = el;
38214                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38215                 */
38216                 
38217                 if (region == 'center' && this.active ) {
38218                     cfg.background = false;
38219                 }
38220                 
38221                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38222                 
38223                 this.add(region, ret);
38224                 /*
38225                 if (cfg.background) {
38226                     // render grid on panel activation (if panel background)
38227                     ret.on('activate', function(gp) {
38228                         if (!gp.grid.rendered) {
38229                     //        gp.grid.render(el);
38230                         }
38231                     });
38232                 } else {
38233                   //  cfg.grid.render(el);
38234                 }
38235                 */
38236                 break;
38237            
38238            
38239             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38240                 // it was the old xcomponent building that caused this before.
38241                 // espeically if border is the top element in the tree.
38242                 ret = this;
38243                 break; 
38244                 
38245                     
38246                 
38247                 
38248                 
38249             default:
38250                 /*
38251                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38252                     
38253                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38254                     this.add(region, ret);
38255                 } else {
38256                 */
38257                     Roo.log(cfg);
38258                     throw "Can not add '" + cfg.xtype + "' to Border";
38259                     return null;
38260              
38261                                 
38262              
38263         }
38264         this.beginUpdate();
38265         // add children..
38266         var region = '';
38267         var abn = {};
38268         Roo.each(xitems, function(i)  {
38269             region = nb && i.region ? i.region : false;
38270             
38271             var add = ret.addxtype(i);
38272            
38273             if (region) {
38274                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38275                 if (!i.background) {
38276                     abn[region] = nb[region] ;
38277                 }
38278             }
38279             
38280         });
38281         this.endUpdate();
38282
38283         // make the last non-background panel active..
38284         //if (nb) { Roo.log(abn); }
38285         if (nb) {
38286             
38287             for(var r in abn) {
38288                 region = this.getRegion(r);
38289                 if (region) {
38290                     // tried using nb[r], but it does not work..
38291                      
38292                     region.showPanel(abn[r]);
38293                    
38294                 }
38295             }
38296         }
38297         return ret;
38298         
38299     },
38300     
38301     
38302 // private
38303     factory : function(cfg)
38304     {
38305         
38306         var validRegions = Roo.bootstrap.layout.Border.regions;
38307
38308         var target = cfg.region;
38309         cfg.mgr = this;
38310         
38311         var r = Roo.bootstrap.layout;
38312         Roo.log(target);
38313         switch(target){
38314             case "north":
38315                 return new r.North(cfg);
38316             case "south":
38317                 return new r.South(cfg);
38318             case "east":
38319                 return new r.East(cfg);
38320             case "west":
38321                 return new r.West(cfg);
38322             case "center":
38323                 return new r.Center(cfg);
38324         }
38325         throw 'Layout region "'+target+'" not supported.';
38326     }
38327     
38328     
38329 });
38330  /*
38331  * Based on:
38332  * Ext JS Library 1.1.1
38333  * Copyright(c) 2006-2007, Ext JS, LLC.
38334  *
38335  * Originally Released Under LGPL - original licence link has changed is not relivant.
38336  *
38337  * Fork - LGPL
38338  * <script type="text/javascript">
38339  */
38340  
38341 /**
38342  * @class Roo.bootstrap.layout.Basic
38343  * @extends Roo.util.Observable
38344  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38345  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38346  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38347  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38348  * @cfg {string}   region  the region that it inhabits..
38349  * @cfg {bool}   skipConfig skip config?
38350  * 
38351
38352  */
38353 Roo.bootstrap.layout.Basic = function(config){
38354     
38355     this.mgr = config.mgr;
38356     
38357     this.position = config.region;
38358     
38359     var skipConfig = config.skipConfig;
38360     
38361     this.events = {
38362         /**
38363          * @scope Roo.BasicLayoutRegion
38364          */
38365         
38366         /**
38367          * @event beforeremove
38368          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38369          * @param {Roo.LayoutRegion} this
38370          * @param {Roo.ContentPanel} panel The panel
38371          * @param {Object} e The cancel event object
38372          */
38373         "beforeremove" : true,
38374         /**
38375          * @event invalidated
38376          * Fires when the layout for this region is changed.
38377          * @param {Roo.LayoutRegion} this
38378          */
38379         "invalidated" : true,
38380         /**
38381          * @event visibilitychange
38382          * Fires when this region is shown or hidden 
38383          * @param {Roo.LayoutRegion} this
38384          * @param {Boolean} visibility true or false
38385          */
38386         "visibilitychange" : true,
38387         /**
38388          * @event paneladded
38389          * Fires when a panel is added. 
38390          * @param {Roo.LayoutRegion} this
38391          * @param {Roo.ContentPanel} panel The panel
38392          */
38393         "paneladded" : true,
38394         /**
38395          * @event panelremoved
38396          * Fires when a panel is removed. 
38397          * @param {Roo.LayoutRegion} this
38398          * @param {Roo.ContentPanel} panel The panel
38399          */
38400         "panelremoved" : true,
38401         /**
38402          * @event beforecollapse
38403          * Fires when this region before collapse.
38404          * @param {Roo.LayoutRegion} this
38405          */
38406         "beforecollapse" : true,
38407         /**
38408          * @event collapsed
38409          * Fires when this region is collapsed.
38410          * @param {Roo.LayoutRegion} this
38411          */
38412         "collapsed" : true,
38413         /**
38414          * @event expanded
38415          * Fires when this region is expanded.
38416          * @param {Roo.LayoutRegion} this
38417          */
38418         "expanded" : true,
38419         /**
38420          * @event slideshow
38421          * Fires when this region is slid into view.
38422          * @param {Roo.LayoutRegion} this
38423          */
38424         "slideshow" : true,
38425         /**
38426          * @event slidehide
38427          * Fires when this region slides out of view. 
38428          * @param {Roo.LayoutRegion} this
38429          */
38430         "slidehide" : true,
38431         /**
38432          * @event panelactivated
38433          * Fires when a panel is activated. 
38434          * @param {Roo.LayoutRegion} this
38435          * @param {Roo.ContentPanel} panel The activated panel
38436          */
38437         "panelactivated" : true,
38438         /**
38439          * @event resized
38440          * Fires when the user resizes this region. 
38441          * @param {Roo.LayoutRegion} this
38442          * @param {Number} newSize The new size (width for east/west, height for north/south)
38443          */
38444         "resized" : true
38445     };
38446     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38447     this.panels = new Roo.util.MixedCollection();
38448     this.panels.getKey = this.getPanelId.createDelegate(this);
38449     this.box = null;
38450     this.activePanel = null;
38451     // ensure listeners are added...
38452     
38453     if (config.listeners || config.events) {
38454         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38455             listeners : config.listeners || {},
38456             events : config.events || {}
38457         });
38458     }
38459     
38460     if(skipConfig !== true){
38461         this.applyConfig(config);
38462     }
38463 };
38464
38465 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38466 {
38467     getPanelId : function(p){
38468         return p.getId();
38469     },
38470     
38471     applyConfig : function(config){
38472         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38473         this.config = config;
38474         
38475     },
38476     
38477     /**
38478      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38479      * the width, for horizontal (north, south) the height.
38480      * @param {Number} newSize The new width or height
38481      */
38482     resizeTo : function(newSize){
38483         var el = this.el ? this.el :
38484                  (this.activePanel ? this.activePanel.getEl() : null);
38485         if(el){
38486             switch(this.position){
38487                 case "east":
38488                 case "west":
38489                     el.setWidth(newSize);
38490                     this.fireEvent("resized", this, newSize);
38491                 break;
38492                 case "north":
38493                 case "south":
38494                     el.setHeight(newSize);
38495                     this.fireEvent("resized", this, newSize);
38496                 break;                
38497             }
38498         }
38499     },
38500     
38501     getBox : function(){
38502         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38503     },
38504     
38505     getMargins : function(){
38506         return this.margins;
38507     },
38508     
38509     updateBox : function(box){
38510         this.box = box;
38511         var el = this.activePanel.getEl();
38512         el.dom.style.left = box.x + "px";
38513         el.dom.style.top = box.y + "px";
38514         this.activePanel.setSize(box.width, box.height);
38515     },
38516     
38517     /**
38518      * Returns the container element for this region.
38519      * @return {Roo.Element}
38520      */
38521     getEl : function(){
38522         return this.activePanel;
38523     },
38524     
38525     /**
38526      * Returns true if this region is currently visible.
38527      * @return {Boolean}
38528      */
38529     isVisible : function(){
38530         return this.activePanel ? true : false;
38531     },
38532     
38533     setActivePanel : function(panel){
38534         panel = this.getPanel(panel);
38535         if(this.activePanel && this.activePanel != panel){
38536             this.activePanel.setActiveState(false);
38537             this.activePanel.getEl().setLeftTop(-10000,-10000);
38538         }
38539         this.activePanel = panel;
38540         panel.setActiveState(true);
38541         if(this.box){
38542             panel.setSize(this.box.width, this.box.height);
38543         }
38544         this.fireEvent("panelactivated", this, panel);
38545         this.fireEvent("invalidated");
38546     },
38547     
38548     /**
38549      * Show the specified panel.
38550      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38551      * @return {Roo.ContentPanel} The shown panel or null
38552      */
38553     showPanel : function(panel){
38554         panel = this.getPanel(panel);
38555         if(panel){
38556             this.setActivePanel(panel);
38557         }
38558         return panel;
38559     },
38560     
38561     /**
38562      * Get the active panel for this region.
38563      * @return {Roo.ContentPanel} The active panel or null
38564      */
38565     getActivePanel : function(){
38566         return this.activePanel;
38567     },
38568     
38569     /**
38570      * Add the passed ContentPanel(s)
38571      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38572      * @return {Roo.ContentPanel} The panel added (if only one was added)
38573      */
38574     add : function(panel){
38575         if(arguments.length > 1){
38576             for(var i = 0, len = arguments.length; i < len; i++) {
38577                 this.add(arguments[i]);
38578             }
38579             return null;
38580         }
38581         if(this.hasPanel(panel)){
38582             this.showPanel(panel);
38583             return panel;
38584         }
38585         var el = panel.getEl();
38586         if(el.dom.parentNode != this.mgr.el.dom){
38587             this.mgr.el.dom.appendChild(el.dom);
38588         }
38589         if(panel.setRegion){
38590             panel.setRegion(this);
38591         }
38592         this.panels.add(panel);
38593         el.setStyle("position", "absolute");
38594         if(!panel.background){
38595             this.setActivePanel(panel);
38596             if(this.config.initialSize && this.panels.getCount()==1){
38597                 this.resizeTo(this.config.initialSize);
38598             }
38599         }
38600         this.fireEvent("paneladded", this, panel);
38601         return panel;
38602     },
38603     
38604     /**
38605      * Returns true if the panel is in this region.
38606      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38607      * @return {Boolean}
38608      */
38609     hasPanel : function(panel){
38610         if(typeof panel == "object"){ // must be panel obj
38611             panel = panel.getId();
38612         }
38613         return this.getPanel(panel) ? true : false;
38614     },
38615     
38616     /**
38617      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38618      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38619      * @param {Boolean} preservePanel Overrides the config preservePanel option
38620      * @return {Roo.ContentPanel} The panel that was removed
38621      */
38622     remove : function(panel, preservePanel){
38623         panel = this.getPanel(panel);
38624         if(!panel){
38625             return null;
38626         }
38627         var e = {};
38628         this.fireEvent("beforeremove", this, panel, e);
38629         if(e.cancel === true){
38630             return null;
38631         }
38632         var panelId = panel.getId();
38633         this.panels.removeKey(panelId);
38634         return panel;
38635     },
38636     
38637     /**
38638      * Returns the panel specified or null if it's not in this region.
38639      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38640      * @return {Roo.ContentPanel}
38641      */
38642     getPanel : function(id){
38643         if(typeof id == "object"){ // must be panel obj
38644             return id;
38645         }
38646         return this.panels.get(id);
38647     },
38648     
38649     /**
38650      * Returns this regions position (north/south/east/west/center).
38651      * @return {String} 
38652      */
38653     getPosition: function(){
38654         return this.position;    
38655     }
38656 });/*
38657  * Based on:
38658  * Ext JS Library 1.1.1
38659  * Copyright(c) 2006-2007, Ext JS, LLC.
38660  *
38661  * Originally Released Under LGPL - original licence link has changed is not relivant.
38662  *
38663  * Fork - LGPL
38664  * <script type="text/javascript">
38665  */
38666  
38667 /**
38668  * @class Roo.bootstrap.layout.Region
38669  * @extends Roo.bootstrap.layout.Basic
38670  * This class represents a region in a layout manager.
38671  
38672  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38673  * @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})
38674  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38675  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38676  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38677  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38678  * @cfg {String}    title           The title for the region (overrides panel titles)
38679  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38680  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38681  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38682  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38683  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38684  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38685  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38686  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38687  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38688  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38689
38690  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38691  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38692  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38693  * @cfg {Number}    width           For East/West panels
38694  * @cfg {Number}    height          For North/South panels
38695  * @cfg {Boolean}   split           To show the splitter
38696  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38697  * 
38698  * @cfg {string}   cls             Extra CSS classes to add to region
38699  * 
38700  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38701  * @cfg {string}   region  the region that it inhabits..
38702  *
38703
38704  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38705  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38706
38707  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38708  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38709  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38710  */
38711 Roo.bootstrap.layout.Region = function(config)
38712 {
38713     this.applyConfig(config);
38714
38715     var mgr = config.mgr;
38716     var pos = config.region;
38717     config.skipConfig = true;
38718     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38719     
38720     if (mgr.el) {
38721         this.onRender(mgr.el);   
38722     }
38723      
38724     this.visible = true;
38725     this.collapsed = false;
38726     this.unrendered_panels = [];
38727 };
38728
38729 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38730
38731     position: '', // set by wrapper (eg. north/south etc..)
38732     unrendered_panels : null,  // unrendered panels.
38733     
38734     tabPosition : false,
38735     
38736     mgr: false, // points to 'Border'
38737     
38738     
38739     createBody : function(){
38740         /** This region's body element 
38741         * @type Roo.Element */
38742         this.bodyEl = this.el.createChild({
38743                 tag: "div",
38744                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38745         });
38746     },
38747
38748     onRender: function(ctr, pos)
38749     {
38750         var dh = Roo.DomHelper;
38751         /** This region's container element 
38752         * @type Roo.Element */
38753         this.el = dh.append(ctr.dom, {
38754                 tag: "div",
38755                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38756             }, true);
38757         /** This region's title element 
38758         * @type Roo.Element */
38759     
38760         this.titleEl = dh.append(this.el.dom,  {
38761                 tag: "div",
38762                 unselectable: "on",
38763                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38764                 children:[
38765                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38766                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38767                 ]
38768             }, true);
38769         
38770         this.titleEl.enableDisplayMode();
38771         /** This region's title text element 
38772         * @type HTMLElement */
38773         this.titleTextEl = this.titleEl.dom.firstChild;
38774         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38775         /*
38776         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38777         this.closeBtn.enableDisplayMode();
38778         this.closeBtn.on("click", this.closeClicked, this);
38779         this.closeBtn.hide();
38780     */
38781         this.createBody(this.config);
38782         if(this.config.hideWhenEmpty){
38783             this.hide();
38784             this.on("paneladded", this.validateVisibility, this);
38785             this.on("panelremoved", this.validateVisibility, this);
38786         }
38787         if(this.autoScroll){
38788             this.bodyEl.setStyle("overflow", "auto");
38789         }else{
38790             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38791         }
38792         //if(c.titlebar !== false){
38793             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38794                 this.titleEl.hide();
38795             }else{
38796                 this.titleEl.show();
38797                 if(this.config.title){
38798                     this.titleTextEl.innerHTML = this.config.title;
38799                 }
38800             }
38801         //}
38802         if(this.config.collapsed){
38803             this.collapse(true);
38804         }
38805         if(this.config.hidden){
38806             this.hide();
38807         }
38808         
38809         if (this.unrendered_panels && this.unrendered_panels.length) {
38810             for (var i =0;i< this.unrendered_panels.length; i++) {
38811                 this.add(this.unrendered_panels[i]);
38812             }
38813             this.unrendered_panels = null;
38814             
38815         }
38816         
38817     },
38818     
38819     applyConfig : function(c)
38820     {
38821         /*
38822          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38823             var dh = Roo.DomHelper;
38824             if(c.titlebar !== false){
38825                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38826                 this.collapseBtn.on("click", this.collapse, this);
38827                 this.collapseBtn.enableDisplayMode();
38828                 /*
38829                 if(c.showPin === true || this.showPin){
38830                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38831                     this.stickBtn.enableDisplayMode();
38832                     this.stickBtn.on("click", this.expand, this);
38833                     this.stickBtn.hide();
38834                 }
38835                 
38836             }
38837             */
38838             /** This region's collapsed element
38839             * @type Roo.Element */
38840             /*
38841              *
38842             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38843                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38844             ]}, true);
38845             
38846             if(c.floatable !== false){
38847                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38848                this.collapsedEl.on("click", this.collapseClick, this);
38849             }
38850
38851             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38852                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38853                    id: "message", unselectable: "on", style:{"float":"left"}});
38854                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38855              }
38856             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38857             this.expandBtn.on("click", this.expand, this);
38858             
38859         }
38860         
38861         if(this.collapseBtn){
38862             this.collapseBtn.setVisible(c.collapsible == true);
38863         }
38864         
38865         this.cmargins = c.cmargins || this.cmargins ||
38866                          (this.position == "west" || this.position == "east" ?
38867                              {top: 0, left: 2, right:2, bottom: 0} :
38868                              {top: 2, left: 0, right:0, bottom: 2});
38869         */
38870         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38871         
38872         
38873         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38874         
38875         this.autoScroll = c.autoScroll || false;
38876         
38877         
38878        
38879         
38880         this.duration = c.duration || .30;
38881         this.slideDuration = c.slideDuration || .45;
38882         this.config = c;
38883        
38884     },
38885     /**
38886      * Returns true if this region is currently visible.
38887      * @return {Boolean}
38888      */
38889     isVisible : function(){
38890         return this.visible;
38891     },
38892
38893     /**
38894      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38895      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38896      */
38897     //setCollapsedTitle : function(title){
38898     //    title = title || "&#160;";
38899      //   if(this.collapsedTitleTextEl){
38900       //      this.collapsedTitleTextEl.innerHTML = title;
38901        // }
38902     //},
38903
38904     getBox : function(){
38905         var b;
38906       //  if(!this.collapsed){
38907             b = this.el.getBox(false, true);
38908        // }else{
38909           //  b = this.collapsedEl.getBox(false, true);
38910         //}
38911         return b;
38912     },
38913
38914     getMargins : function(){
38915         return this.margins;
38916         //return this.collapsed ? this.cmargins : this.margins;
38917     },
38918 /*
38919     highlight : function(){
38920         this.el.addClass("x-layout-panel-dragover");
38921     },
38922
38923     unhighlight : function(){
38924         this.el.removeClass("x-layout-panel-dragover");
38925     },
38926 */
38927     updateBox : function(box)
38928     {
38929         if (!this.bodyEl) {
38930             return; // not rendered yet..
38931         }
38932         
38933         this.box = box;
38934         if(!this.collapsed){
38935             this.el.dom.style.left = box.x + "px";
38936             this.el.dom.style.top = box.y + "px";
38937             this.updateBody(box.width, box.height);
38938         }else{
38939             this.collapsedEl.dom.style.left = box.x + "px";
38940             this.collapsedEl.dom.style.top = box.y + "px";
38941             this.collapsedEl.setSize(box.width, box.height);
38942         }
38943         if(this.tabs){
38944             this.tabs.autoSizeTabs();
38945         }
38946     },
38947
38948     updateBody : function(w, h)
38949     {
38950         if(w !== null){
38951             this.el.setWidth(w);
38952             w -= this.el.getBorderWidth("rl");
38953             if(this.config.adjustments){
38954                 w += this.config.adjustments[0];
38955             }
38956         }
38957         if(h !== null && h > 0){
38958             this.el.setHeight(h);
38959             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38960             h -= this.el.getBorderWidth("tb");
38961             if(this.config.adjustments){
38962                 h += this.config.adjustments[1];
38963             }
38964             this.bodyEl.setHeight(h);
38965             if(this.tabs){
38966                 h = this.tabs.syncHeight(h);
38967             }
38968         }
38969         if(this.panelSize){
38970             w = w !== null ? w : this.panelSize.width;
38971             h = h !== null ? h : this.panelSize.height;
38972         }
38973         if(this.activePanel){
38974             var el = this.activePanel.getEl();
38975             w = w !== null ? w : el.getWidth();
38976             h = h !== null ? h : el.getHeight();
38977             this.panelSize = {width: w, height: h};
38978             this.activePanel.setSize(w, h);
38979         }
38980         if(Roo.isIE && this.tabs){
38981             this.tabs.el.repaint();
38982         }
38983     },
38984
38985     /**
38986      * Returns the container element for this region.
38987      * @return {Roo.Element}
38988      */
38989     getEl : function(){
38990         return this.el;
38991     },
38992
38993     /**
38994      * Hides this region.
38995      */
38996     hide : function(){
38997         //if(!this.collapsed){
38998             this.el.dom.style.left = "-2000px";
38999             this.el.hide();
39000         //}else{
39001          //   this.collapsedEl.dom.style.left = "-2000px";
39002          //   this.collapsedEl.hide();
39003        // }
39004         this.visible = false;
39005         this.fireEvent("visibilitychange", this, false);
39006     },
39007
39008     /**
39009      * Shows this region if it was previously hidden.
39010      */
39011     show : function(){
39012         //if(!this.collapsed){
39013             this.el.show();
39014         //}else{
39015         //    this.collapsedEl.show();
39016        // }
39017         this.visible = true;
39018         this.fireEvent("visibilitychange", this, true);
39019     },
39020 /*
39021     closeClicked : function(){
39022         if(this.activePanel){
39023             this.remove(this.activePanel);
39024         }
39025     },
39026
39027     collapseClick : function(e){
39028         if(this.isSlid){
39029            e.stopPropagation();
39030            this.slideIn();
39031         }else{
39032            e.stopPropagation();
39033            this.slideOut();
39034         }
39035     },
39036 */
39037     /**
39038      * Collapses this region.
39039      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39040      */
39041     /*
39042     collapse : function(skipAnim, skipCheck = false){
39043         if(this.collapsed) {
39044             return;
39045         }
39046         
39047         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39048             
39049             this.collapsed = true;
39050             if(this.split){
39051                 this.split.el.hide();
39052             }
39053             if(this.config.animate && skipAnim !== true){
39054                 this.fireEvent("invalidated", this);
39055                 this.animateCollapse();
39056             }else{
39057                 this.el.setLocation(-20000,-20000);
39058                 this.el.hide();
39059                 this.collapsedEl.show();
39060                 this.fireEvent("collapsed", this);
39061                 this.fireEvent("invalidated", this);
39062             }
39063         }
39064         
39065     },
39066 */
39067     animateCollapse : function(){
39068         // overridden
39069     },
39070
39071     /**
39072      * Expands this region if it was previously collapsed.
39073      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39074      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39075      */
39076     /*
39077     expand : function(e, skipAnim){
39078         if(e) {
39079             e.stopPropagation();
39080         }
39081         if(!this.collapsed || this.el.hasActiveFx()) {
39082             return;
39083         }
39084         if(this.isSlid){
39085             this.afterSlideIn();
39086             skipAnim = true;
39087         }
39088         this.collapsed = false;
39089         if(this.config.animate && skipAnim !== true){
39090             this.animateExpand();
39091         }else{
39092             this.el.show();
39093             if(this.split){
39094                 this.split.el.show();
39095             }
39096             this.collapsedEl.setLocation(-2000,-2000);
39097             this.collapsedEl.hide();
39098             this.fireEvent("invalidated", this);
39099             this.fireEvent("expanded", this);
39100         }
39101     },
39102 */
39103     animateExpand : function(){
39104         // overridden
39105     },
39106
39107     initTabs : function()
39108     {
39109         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39110         
39111         var ts = new Roo.bootstrap.panel.Tabs({
39112             el: this.bodyEl.dom,
39113             region : this,
39114             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39115             disableTooltips: this.config.disableTabTips,
39116             toolbar : this.config.toolbar
39117         });
39118         
39119         if(this.config.hideTabs){
39120             ts.stripWrap.setDisplayed(false);
39121         }
39122         this.tabs = ts;
39123         ts.resizeTabs = this.config.resizeTabs === true;
39124         ts.minTabWidth = this.config.minTabWidth || 40;
39125         ts.maxTabWidth = this.config.maxTabWidth || 250;
39126         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39127         ts.monitorResize = false;
39128         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39129         ts.bodyEl.addClass('roo-layout-tabs-body');
39130         this.panels.each(this.initPanelAsTab, this);
39131     },
39132
39133     initPanelAsTab : function(panel){
39134         var ti = this.tabs.addTab(
39135             panel.getEl().id,
39136             panel.getTitle(),
39137             null,
39138             this.config.closeOnTab && panel.isClosable(),
39139             panel.tpl
39140         );
39141         if(panel.tabTip !== undefined){
39142             ti.setTooltip(panel.tabTip);
39143         }
39144         ti.on("activate", function(){
39145               this.setActivePanel(panel);
39146         }, this);
39147         
39148         if(this.config.closeOnTab){
39149             ti.on("beforeclose", function(t, e){
39150                 e.cancel = true;
39151                 this.remove(panel);
39152             }, this);
39153         }
39154         
39155         panel.tabItem = ti;
39156         
39157         return ti;
39158     },
39159
39160     updatePanelTitle : function(panel, title)
39161     {
39162         if(this.activePanel == panel){
39163             this.updateTitle(title);
39164         }
39165         if(this.tabs){
39166             var ti = this.tabs.getTab(panel.getEl().id);
39167             ti.setText(title);
39168             if(panel.tabTip !== undefined){
39169                 ti.setTooltip(panel.tabTip);
39170             }
39171         }
39172     },
39173
39174     updateTitle : function(title){
39175         if(this.titleTextEl && !this.config.title){
39176             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39177         }
39178     },
39179
39180     setActivePanel : function(panel)
39181     {
39182         panel = this.getPanel(panel);
39183         if(this.activePanel && this.activePanel != panel){
39184             if(this.activePanel.setActiveState(false) === false){
39185                 return;
39186             }
39187         }
39188         this.activePanel = panel;
39189         panel.setActiveState(true);
39190         if(this.panelSize){
39191             panel.setSize(this.panelSize.width, this.panelSize.height);
39192         }
39193         if(this.closeBtn){
39194             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39195         }
39196         this.updateTitle(panel.getTitle());
39197         if(this.tabs){
39198             this.fireEvent("invalidated", this);
39199         }
39200         this.fireEvent("panelactivated", this, panel);
39201     },
39202
39203     /**
39204      * Shows the specified panel.
39205      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39206      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39207      */
39208     showPanel : function(panel)
39209     {
39210         panel = this.getPanel(panel);
39211         if(panel){
39212             if(this.tabs){
39213                 var tab = this.tabs.getTab(panel.getEl().id);
39214                 if(tab.isHidden()){
39215                     this.tabs.unhideTab(tab.id);
39216                 }
39217                 tab.activate();
39218             }else{
39219                 this.setActivePanel(panel);
39220             }
39221         }
39222         return panel;
39223     },
39224
39225     /**
39226      * Get the active panel for this region.
39227      * @return {Roo.ContentPanel} The active panel or null
39228      */
39229     getActivePanel : function(){
39230         return this.activePanel;
39231     },
39232
39233     validateVisibility : function(){
39234         if(this.panels.getCount() < 1){
39235             this.updateTitle("&#160;");
39236             this.closeBtn.hide();
39237             this.hide();
39238         }else{
39239             if(!this.isVisible()){
39240                 this.show();
39241             }
39242         }
39243     },
39244
39245     /**
39246      * Adds the passed ContentPanel(s) to this region.
39247      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39248      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39249      */
39250     add : function(panel)
39251     {
39252         if(arguments.length > 1){
39253             for(var i = 0, len = arguments.length; i < len; i++) {
39254                 this.add(arguments[i]);
39255             }
39256             return null;
39257         }
39258         
39259         // if we have not been rendered yet, then we can not really do much of this..
39260         if (!this.bodyEl) {
39261             this.unrendered_panels.push(panel);
39262             return panel;
39263         }
39264         
39265         
39266         
39267         
39268         if(this.hasPanel(panel)){
39269             this.showPanel(panel);
39270             return panel;
39271         }
39272         panel.setRegion(this);
39273         this.panels.add(panel);
39274        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39275             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39276             // and hide them... ???
39277             this.bodyEl.dom.appendChild(panel.getEl().dom);
39278             if(panel.background !== true){
39279                 this.setActivePanel(panel);
39280             }
39281             this.fireEvent("paneladded", this, panel);
39282             return panel;
39283         }
39284         */
39285         if(!this.tabs){
39286             this.initTabs();
39287         }else{
39288             this.initPanelAsTab(panel);
39289         }
39290         
39291         
39292         if(panel.background !== true){
39293             this.tabs.activate(panel.getEl().id);
39294         }
39295         this.fireEvent("paneladded", this, panel);
39296         return panel;
39297     },
39298
39299     /**
39300      * Hides the tab for the specified panel.
39301      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39302      */
39303     hidePanel : function(panel){
39304         if(this.tabs && (panel = this.getPanel(panel))){
39305             this.tabs.hideTab(panel.getEl().id);
39306         }
39307     },
39308
39309     /**
39310      * Unhides the tab for a previously hidden panel.
39311      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39312      */
39313     unhidePanel : function(panel){
39314         if(this.tabs && (panel = this.getPanel(panel))){
39315             this.tabs.unhideTab(panel.getEl().id);
39316         }
39317     },
39318
39319     clearPanels : function(){
39320         while(this.panels.getCount() > 0){
39321              this.remove(this.panels.first());
39322         }
39323     },
39324
39325     /**
39326      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39327      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39328      * @param {Boolean} preservePanel Overrides the config preservePanel option
39329      * @return {Roo.ContentPanel} The panel that was removed
39330      */
39331     remove : function(panel, preservePanel)
39332     {
39333         panel = this.getPanel(panel);
39334         if(!panel){
39335             return null;
39336         }
39337         var e = {};
39338         this.fireEvent("beforeremove", this, panel, e);
39339         if(e.cancel === true){
39340             return null;
39341         }
39342         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39343         var panelId = panel.getId();
39344         this.panels.removeKey(panelId);
39345         if(preservePanel){
39346             document.body.appendChild(panel.getEl().dom);
39347         }
39348         if(this.tabs){
39349             this.tabs.removeTab(panel.getEl().id);
39350         }else if (!preservePanel){
39351             this.bodyEl.dom.removeChild(panel.getEl().dom);
39352         }
39353         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39354             var p = this.panels.first();
39355             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39356             tempEl.appendChild(p.getEl().dom);
39357             this.bodyEl.update("");
39358             this.bodyEl.dom.appendChild(p.getEl().dom);
39359             tempEl = null;
39360             this.updateTitle(p.getTitle());
39361             this.tabs = null;
39362             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39363             this.setActivePanel(p);
39364         }
39365         panel.setRegion(null);
39366         if(this.activePanel == panel){
39367             this.activePanel = null;
39368         }
39369         if(this.config.autoDestroy !== false && preservePanel !== true){
39370             try{panel.destroy();}catch(e){}
39371         }
39372         this.fireEvent("panelremoved", this, panel);
39373         return panel;
39374     },
39375
39376     /**
39377      * Returns the TabPanel component used by this region
39378      * @return {Roo.TabPanel}
39379      */
39380     getTabs : function(){
39381         return this.tabs;
39382     },
39383
39384     createTool : function(parentEl, className){
39385         var btn = Roo.DomHelper.append(parentEl, {
39386             tag: "div",
39387             cls: "x-layout-tools-button",
39388             children: [ {
39389                 tag: "div",
39390                 cls: "roo-layout-tools-button-inner " + className,
39391                 html: "&#160;"
39392             }]
39393         }, true);
39394         btn.addClassOnOver("roo-layout-tools-button-over");
39395         return btn;
39396     }
39397 });/*
39398  * Based on:
39399  * Ext JS Library 1.1.1
39400  * Copyright(c) 2006-2007, Ext JS, LLC.
39401  *
39402  * Originally Released Under LGPL - original licence link has changed is not relivant.
39403  *
39404  * Fork - LGPL
39405  * <script type="text/javascript">
39406  */
39407  
39408
39409
39410 /**
39411  * @class Roo.SplitLayoutRegion
39412  * @extends Roo.LayoutRegion
39413  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39414  */
39415 Roo.bootstrap.layout.Split = function(config){
39416     this.cursor = config.cursor;
39417     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39418 };
39419
39420 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39421 {
39422     splitTip : "Drag to resize.",
39423     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39424     useSplitTips : false,
39425
39426     applyConfig : function(config){
39427         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39428     },
39429     
39430     onRender : function(ctr,pos) {
39431         
39432         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39433         if(!this.config.split){
39434             return;
39435         }
39436         if(!this.split){
39437             
39438             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39439                             tag: "div",
39440                             id: this.el.id + "-split",
39441                             cls: "roo-layout-split roo-layout-split-"+this.position,
39442                             html: "&#160;"
39443             });
39444             /** The SplitBar for this region 
39445             * @type Roo.SplitBar */
39446             // does not exist yet...
39447             Roo.log([this.position, this.orientation]);
39448             
39449             this.split = new Roo.bootstrap.SplitBar({
39450                 dragElement : splitEl,
39451                 resizingElement: this.el,
39452                 orientation : this.orientation
39453             });
39454             
39455             this.split.on("moved", this.onSplitMove, this);
39456             this.split.useShim = this.config.useShim === true;
39457             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39458             if(this.useSplitTips){
39459                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39460             }
39461             //if(config.collapsible){
39462             //    this.split.el.on("dblclick", this.collapse,  this);
39463             //}
39464         }
39465         if(typeof this.config.minSize != "undefined"){
39466             this.split.minSize = this.config.minSize;
39467         }
39468         if(typeof this.config.maxSize != "undefined"){
39469             this.split.maxSize = this.config.maxSize;
39470         }
39471         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39472             this.hideSplitter();
39473         }
39474         
39475     },
39476
39477     getHMaxSize : function(){
39478          var cmax = this.config.maxSize || 10000;
39479          var center = this.mgr.getRegion("center");
39480          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39481     },
39482
39483     getVMaxSize : function(){
39484          var cmax = this.config.maxSize || 10000;
39485          var center = this.mgr.getRegion("center");
39486          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39487     },
39488
39489     onSplitMove : function(split, newSize){
39490         this.fireEvent("resized", this, newSize);
39491     },
39492     
39493     /** 
39494      * Returns the {@link Roo.SplitBar} for this region.
39495      * @return {Roo.SplitBar}
39496      */
39497     getSplitBar : function(){
39498         return this.split;
39499     },
39500     
39501     hide : function(){
39502         this.hideSplitter();
39503         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39504     },
39505
39506     hideSplitter : function(){
39507         if(this.split){
39508             this.split.el.setLocation(-2000,-2000);
39509             this.split.el.hide();
39510         }
39511     },
39512
39513     show : function(){
39514         if(this.split){
39515             this.split.el.show();
39516         }
39517         Roo.bootstrap.layout.Split.superclass.show.call(this);
39518     },
39519     
39520     beforeSlide: function(){
39521         if(Roo.isGecko){// firefox overflow auto bug workaround
39522             this.bodyEl.clip();
39523             if(this.tabs) {
39524                 this.tabs.bodyEl.clip();
39525             }
39526             if(this.activePanel){
39527                 this.activePanel.getEl().clip();
39528                 
39529                 if(this.activePanel.beforeSlide){
39530                     this.activePanel.beforeSlide();
39531                 }
39532             }
39533         }
39534     },
39535     
39536     afterSlide : function(){
39537         if(Roo.isGecko){// firefox overflow auto bug workaround
39538             this.bodyEl.unclip();
39539             if(this.tabs) {
39540                 this.tabs.bodyEl.unclip();
39541             }
39542             if(this.activePanel){
39543                 this.activePanel.getEl().unclip();
39544                 if(this.activePanel.afterSlide){
39545                     this.activePanel.afterSlide();
39546                 }
39547             }
39548         }
39549     },
39550
39551     initAutoHide : function(){
39552         if(this.autoHide !== false){
39553             if(!this.autoHideHd){
39554                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39555                 this.autoHideHd = {
39556                     "mouseout": function(e){
39557                         if(!e.within(this.el, true)){
39558                             st.delay(500);
39559                         }
39560                     },
39561                     "mouseover" : function(e){
39562                         st.cancel();
39563                     },
39564                     scope : this
39565                 };
39566             }
39567             this.el.on(this.autoHideHd);
39568         }
39569     },
39570
39571     clearAutoHide : function(){
39572         if(this.autoHide !== false){
39573             this.el.un("mouseout", this.autoHideHd.mouseout);
39574             this.el.un("mouseover", this.autoHideHd.mouseover);
39575         }
39576     },
39577
39578     clearMonitor : function(){
39579         Roo.get(document).un("click", this.slideInIf, this);
39580     },
39581
39582     // these names are backwards but not changed for compat
39583     slideOut : function(){
39584         if(this.isSlid || this.el.hasActiveFx()){
39585             return;
39586         }
39587         this.isSlid = true;
39588         if(this.collapseBtn){
39589             this.collapseBtn.hide();
39590         }
39591         this.closeBtnState = this.closeBtn.getStyle('display');
39592         this.closeBtn.hide();
39593         if(this.stickBtn){
39594             this.stickBtn.show();
39595         }
39596         this.el.show();
39597         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39598         this.beforeSlide();
39599         this.el.setStyle("z-index", 10001);
39600         this.el.slideIn(this.getSlideAnchor(), {
39601             callback: function(){
39602                 this.afterSlide();
39603                 this.initAutoHide();
39604                 Roo.get(document).on("click", this.slideInIf, this);
39605                 this.fireEvent("slideshow", this);
39606             },
39607             scope: this,
39608             block: true
39609         });
39610     },
39611
39612     afterSlideIn : function(){
39613         this.clearAutoHide();
39614         this.isSlid = false;
39615         this.clearMonitor();
39616         this.el.setStyle("z-index", "");
39617         if(this.collapseBtn){
39618             this.collapseBtn.show();
39619         }
39620         this.closeBtn.setStyle('display', this.closeBtnState);
39621         if(this.stickBtn){
39622             this.stickBtn.hide();
39623         }
39624         this.fireEvent("slidehide", this);
39625     },
39626
39627     slideIn : function(cb){
39628         if(!this.isSlid || this.el.hasActiveFx()){
39629             Roo.callback(cb);
39630             return;
39631         }
39632         this.isSlid = false;
39633         this.beforeSlide();
39634         this.el.slideOut(this.getSlideAnchor(), {
39635             callback: function(){
39636                 this.el.setLeftTop(-10000, -10000);
39637                 this.afterSlide();
39638                 this.afterSlideIn();
39639                 Roo.callback(cb);
39640             },
39641             scope: this,
39642             block: true
39643         });
39644     },
39645     
39646     slideInIf : function(e){
39647         if(!e.within(this.el)){
39648             this.slideIn();
39649         }
39650     },
39651
39652     animateCollapse : function(){
39653         this.beforeSlide();
39654         this.el.setStyle("z-index", 20000);
39655         var anchor = this.getSlideAnchor();
39656         this.el.slideOut(anchor, {
39657             callback : function(){
39658                 this.el.setStyle("z-index", "");
39659                 this.collapsedEl.slideIn(anchor, {duration:.3});
39660                 this.afterSlide();
39661                 this.el.setLocation(-10000,-10000);
39662                 this.el.hide();
39663                 this.fireEvent("collapsed", this);
39664             },
39665             scope: this,
39666             block: true
39667         });
39668     },
39669
39670     animateExpand : function(){
39671         this.beforeSlide();
39672         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39673         this.el.setStyle("z-index", 20000);
39674         this.collapsedEl.hide({
39675             duration:.1
39676         });
39677         this.el.slideIn(this.getSlideAnchor(), {
39678             callback : function(){
39679                 this.el.setStyle("z-index", "");
39680                 this.afterSlide();
39681                 if(this.split){
39682                     this.split.el.show();
39683                 }
39684                 this.fireEvent("invalidated", this);
39685                 this.fireEvent("expanded", this);
39686             },
39687             scope: this,
39688             block: true
39689         });
39690     },
39691
39692     anchors : {
39693         "west" : "left",
39694         "east" : "right",
39695         "north" : "top",
39696         "south" : "bottom"
39697     },
39698
39699     sanchors : {
39700         "west" : "l",
39701         "east" : "r",
39702         "north" : "t",
39703         "south" : "b"
39704     },
39705
39706     canchors : {
39707         "west" : "tl-tr",
39708         "east" : "tr-tl",
39709         "north" : "tl-bl",
39710         "south" : "bl-tl"
39711     },
39712
39713     getAnchor : function(){
39714         return this.anchors[this.position];
39715     },
39716
39717     getCollapseAnchor : function(){
39718         return this.canchors[this.position];
39719     },
39720
39721     getSlideAnchor : function(){
39722         return this.sanchors[this.position];
39723     },
39724
39725     getAlignAdj : function(){
39726         var cm = this.cmargins;
39727         switch(this.position){
39728             case "west":
39729                 return [0, 0];
39730             break;
39731             case "east":
39732                 return [0, 0];
39733             break;
39734             case "north":
39735                 return [0, 0];
39736             break;
39737             case "south":
39738                 return [0, 0];
39739             break;
39740         }
39741     },
39742
39743     getExpandAdj : function(){
39744         var c = this.collapsedEl, cm = this.cmargins;
39745         switch(this.position){
39746             case "west":
39747                 return [-(cm.right+c.getWidth()+cm.left), 0];
39748             break;
39749             case "east":
39750                 return [cm.right+c.getWidth()+cm.left, 0];
39751             break;
39752             case "north":
39753                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39754             break;
39755             case "south":
39756                 return [0, cm.top+cm.bottom+c.getHeight()];
39757             break;
39758         }
39759     }
39760 });/*
39761  * Based on:
39762  * Ext JS Library 1.1.1
39763  * Copyright(c) 2006-2007, Ext JS, LLC.
39764  *
39765  * Originally Released Under LGPL - original licence link has changed is not relivant.
39766  *
39767  * Fork - LGPL
39768  * <script type="text/javascript">
39769  */
39770 /*
39771  * These classes are private internal classes
39772  */
39773 Roo.bootstrap.layout.Center = function(config){
39774     config.region = "center";
39775     Roo.bootstrap.layout.Region.call(this, config);
39776     this.visible = true;
39777     this.minWidth = config.minWidth || 20;
39778     this.minHeight = config.minHeight || 20;
39779 };
39780
39781 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39782     hide : function(){
39783         // center panel can't be hidden
39784     },
39785     
39786     show : function(){
39787         // center panel can't be hidden
39788     },
39789     
39790     getMinWidth: function(){
39791         return this.minWidth;
39792     },
39793     
39794     getMinHeight: function(){
39795         return this.minHeight;
39796     }
39797 });
39798
39799
39800
39801
39802  
39803
39804
39805
39806
39807
39808
39809 Roo.bootstrap.layout.North = function(config)
39810 {
39811     config.region = 'north';
39812     config.cursor = 'n-resize';
39813     
39814     Roo.bootstrap.layout.Split.call(this, config);
39815     
39816     
39817     if(this.split){
39818         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39819         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39820         this.split.el.addClass("roo-layout-split-v");
39821     }
39822     //var size = config.initialSize || config.height;
39823     //if(this.el && typeof size != "undefined"){
39824     //    this.el.setHeight(size);
39825     //}
39826 };
39827 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39828 {
39829     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39830      
39831      
39832     onRender : function(ctr, pos)
39833     {
39834         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39835         var size = this.config.initialSize || this.config.height;
39836         if(this.el && typeof size != "undefined"){
39837             this.el.setHeight(size);
39838         }
39839     
39840     },
39841     
39842     getBox : function(){
39843         if(this.collapsed){
39844             return this.collapsedEl.getBox();
39845         }
39846         var box = this.el.getBox();
39847         if(this.split){
39848             box.height += this.split.el.getHeight();
39849         }
39850         return box;
39851     },
39852     
39853     updateBox : function(box){
39854         if(this.split && !this.collapsed){
39855             box.height -= this.split.el.getHeight();
39856             this.split.el.setLeft(box.x);
39857             this.split.el.setTop(box.y+box.height);
39858             this.split.el.setWidth(box.width);
39859         }
39860         if(this.collapsed){
39861             this.updateBody(box.width, null);
39862         }
39863         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39864     }
39865 });
39866
39867
39868
39869
39870
39871 Roo.bootstrap.layout.South = function(config){
39872     config.region = 'south';
39873     config.cursor = 's-resize';
39874     Roo.bootstrap.layout.Split.call(this, config);
39875     if(this.split){
39876         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39877         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39878         this.split.el.addClass("roo-layout-split-v");
39879     }
39880     
39881 };
39882
39883 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39884     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39885     
39886     onRender : function(ctr, pos)
39887     {
39888         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39889         var size = this.config.initialSize || this.config.height;
39890         if(this.el && typeof size != "undefined"){
39891             this.el.setHeight(size);
39892         }
39893     
39894     },
39895     
39896     getBox : function(){
39897         if(this.collapsed){
39898             return this.collapsedEl.getBox();
39899         }
39900         var box = this.el.getBox();
39901         if(this.split){
39902             var sh = this.split.el.getHeight();
39903             box.height += sh;
39904             box.y -= sh;
39905         }
39906         return box;
39907     },
39908     
39909     updateBox : function(box){
39910         if(this.split && !this.collapsed){
39911             var sh = this.split.el.getHeight();
39912             box.height -= sh;
39913             box.y += sh;
39914             this.split.el.setLeft(box.x);
39915             this.split.el.setTop(box.y-sh);
39916             this.split.el.setWidth(box.width);
39917         }
39918         if(this.collapsed){
39919             this.updateBody(box.width, null);
39920         }
39921         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39922     }
39923 });
39924
39925 Roo.bootstrap.layout.East = function(config){
39926     config.region = "east";
39927     config.cursor = "e-resize";
39928     Roo.bootstrap.layout.Split.call(this, config);
39929     if(this.split){
39930         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39931         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39932         this.split.el.addClass("roo-layout-split-h");
39933     }
39934     
39935 };
39936 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39937     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39938     
39939     onRender : function(ctr, pos)
39940     {
39941         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39942         var size = this.config.initialSize || this.config.width;
39943         if(this.el && typeof size != "undefined"){
39944             this.el.setWidth(size);
39945         }
39946     
39947     },
39948     
39949     getBox : function(){
39950         if(this.collapsed){
39951             return this.collapsedEl.getBox();
39952         }
39953         var box = this.el.getBox();
39954         if(this.split){
39955             var sw = this.split.el.getWidth();
39956             box.width += sw;
39957             box.x -= sw;
39958         }
39959         return box;
39960     },
39961
39962     updateBox : function(box){
39963         if(this.split && !this.collapsed){
39964             var sw = this.split.el.getWidth();
39965             box.width -= sw;
39966             this.split.el.setLeft(box.x);
39967             this.split.el.setTop(box.y);
39968             this.split.el.setHeight(box.height);
39969             box.x += sw;
39970         }
39971         if(this.collapsed){
39972             this.updateBody(null, box.height);
39973         }
39974         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39975     }
39976 });
39977
39978 Roo.bootstrap.layout.West = function(config){
39979     config.region = "west";
39980     config.cursor = "w-resize";
39981     
39982     Roo.bootstrap.layout.Split.call(this, config);
39983     if(this.split){
39984         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39985         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39986         this.split.el.addClass("roo-layout-split-h");
39987     }
39988     
39989 };
39990 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39991     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39992     
39993     onRender: function(ctr, pos)
39994     {
39995         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39996         var size = this.config.initialSize || this.config.width;
39997         if(typeof size != "undefined"){
39998             this.el.setWidth(size);
39999         }
40000     },
40001     
40002     getBox : function(){
40003         if(this.collapsed){
40004             return this.collapsedEl.getBox();
40005         }
40006         var box = this.el.getBox();
40007         if (box.width == 0) {
40008             box.width = this.config.width; // kludge?
40009         }
40010         if(this.split){
40011             box.width += this.split.el.getWidth();
40012         }
40013         return box;
40014     },
40015     
40016     updateBox : function(box){
40017         if(this.split && !this.collapsed){
40018             var sw = this.split.el.getWidth();
40019             box.width -= sw;
40020             this.split.el.setLeft(box.x+box.width);
40021             this.split.el.setTop(box.y);
40022             this.split.el.setHeight(box.height);
40023         }
40024         if(this.collapsed){
40025             this.updateBody(null, box.height);
40026         }
40027         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40028     }
40029 });Roo.namespace("Roo.bootstrap.panel");/*
40030  * Based on:
40031  * Ext JS Library 1.1.1
40032  * Copyright(c) 2006-2007, Ext JS, LLC.
40033  *
40034  * Originally Released Under LGPL - original licence link has changed is not relivant.
40035  *
40036  * Fork - LGPL
40037  * <script type="text/javascript">
40038  */
40039 /**
40040  * @class Roo.ContentPanel
40041  * @extends Roo.util.Observable
40042  * A basic ContentPanel element.
40043  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40044  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40045  * @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
40046  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40047  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40048  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40049  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40050  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40051  * @cfg {String} title          The title for this panel
40052  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40053  * @cfg {String} url            Calls {@link #setUrl} with this value
40054  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40055  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40056  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40057  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40058  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40059  * @cfg {Boolean} badges render the badges
40060  * @cfg {String} cls  extra classes to use  
40061  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40062
40063  * @constructor
40064  * Create a new ContentPanel.
40065  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40066  * @param {String/Object} config A string to set only the title or a config object
40067  * @param {String} content (optional) Set the HTML content for this panel
40068  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40069  */
40070 Roo.bootstrap.panel.Content = function( config){
40071     
40072     this.tpl = config.tpl || false;
40073     
40074     var el = config.el;
40075     var content = config.content;
40076
40077     if(config.autoCreate){ // xtype is available if this is called from factory
40078         el = Roo.id();
40079     }
40080     this.el = Roo.get(el);
40081     if(!this.el && config && config.autoCreate){
40082         if(typeof config.autoCreate == "object"){
40083             if(!config.autoCreate.id){
40084                 config.autoCreate.id = config.id||el;
40085             }
40086             this.el = Roo.DomHelper.append(document.body,
40087                         config.autoCreate, true);
40088         }else{
40089             var elcfg =  {
40090                 tag: "div",
40091                 cls: (config.cls || '') +
40092                     (config.background ? ' bg-' + config.background : '') +
40093                     " roo-layout-inactive-content",
40094                 id: config.id||el
40095             };
40096             if (config.iframe) {
40097                 elcfg.cn = [
40098                     {
40099                         tag : 'iframe',
40100                         style : 'border: 0px',
40101                         src : 'about:blank'
40102                     }
40103                 ];
40104             }
40105               
40106             if (config.html) {
40107                 elcfg.html = config.html;
40108                 
40109             }
40110                         
40111             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40112             if (config.iframe) {
40113                 this.iframeEl = this.el.select('iframe',true).first();
40114             }
40115             
40116         }
40117     } 
40118     this.closable = false;
40119     this.loaded = false;
40120     this.active = false;
40121    
40122       
40123     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40124         
40125         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40126         
40127         this.wrapEl = this.el; //this.el.wrap();
40128         var ti = [];
40129         if (config.toolbar.items) {
40130             ti = config.toolbar.items ;
40131             delete config.toolbar.items ;
40132         }
40133         
40134         var nitems = [];
40135         this.toolbar.render(this.wrapEl, 'before');
40136         for(var i =0;i < ti.length;i++) {
40137           //  Roo.log(['add child', items[i]]);
40138             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40139         }
40140         this.toolbar.items = nitems;
40141         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40142         delete config.toolbar;
40143         
40144     }
40145     /*
40146     // xtype created footer. - not sure if will work as we normally have to render first..
40147     if (this.footer && !this.footer.el && this.footer.xtype) {
40148         if (!this.wrapEl) {
40149             this.wrapEl = this.el.wrap();
40150         }
40151     
40152         this.footer.container = this.wrapEl.createChild();
40153          
40154         this.footer = Roo.factory(this.footer, Roo);
40155         
40156     }
40157     */
40158     
40159      if(typeof config == "string"){
40160         this.title = config;
40161     }else{
40162         Roo.apply(this, config);
40163     }
40164     
40165     if(this.resizeEl){
40166         this.resizeEl = Roo.get(this.resizeEl, true);
40167     }else{
40168         this.resizeEl = this.el;
40169     }
40170     // handle view.xtype
40171     
40172  
40173     
40174     
40175     this.addEvents({
40176         /**
40177          * @event activate
40178          * Fires when this panel is activated. 
40179          * @param {Roo.ContentPanel} this
40180          */
40181         "activate" : true,
40182         /**
40183          * @event deactivate
40184          * Fires when this panel is activated. 
40185          * @param {Roo.ContentPanel} this
40186          */
40187         "deactivate" : true,
40188
40189         /**
40190          * @event resize
40191          * Fires when this panel is resized if fitToFrame is true.
40192          * @param {Roo.ContentPanel} this
40193          * @param {Number} width The width after any component adjustments
40194          * @param {Number} height The height after any component adjustments
40195          */
40196         "resize" : true,
40197         
40198          /**
40199          * @event render
40200          * Fires when this tab is created
40201          * @param {Roo.ContentPanel} this
40202          */
40203         "render" : true
40204         
40205         
40206         
40207     });
40208     
40209
40210     
40211     
40212     if(this.autoScroll && !this.iframe){
40213         this.resizeEl.setStyle("overflow", "auto");
40214     } else {
40215         // fix randome scrolling
40216         //this.el.on('scroll', function() {
40217         //    Roo.log('fix random scolling');
40218         //    this.scrollTo('top',0); 
40219         //});
40220     }
40221     content = content || this.content;
40222     if(content){
40223         this.setContent(content);
40224     }
40225     if(config && config.url){
40226         this.setUrl(this.url, this.params, this.loadOnce);
40227     }
40228     
40229     
40230     
40231     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40232     
40233     if (this.view && typeof(this.view.xtype) != 'undefined') {
40234         this.view.el = this.el.appendChild(document.createElement("div"));
40235         this.view = Roo.factory(this.view); 
40236         this.view.render  &&  this.view.render(false, '');  
40237     }
40238     
40239     
40240     this.fireEvent('render', this);
40241 };
40242
40243 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40244     
40245     cls : '',
40246     background : '',
40247     
40248     tabTip : '',
40249     
40250     iframe : false,
40251     iframeEl : false,
40252     
40253     setRegion : function(region){
40254         this.region = region;
40255         this.setActiveClass(region && !this.background);
40256     },
40257     
40258     
40259     setActiveClass: function(state)
40260     {
40261         if(state){
40262            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40263            this.el.setStyle('position','relative');
40264         }else{
40265            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40266            this.el.setStyle('position', 'absolute');
40267         } 
40268     },
40269     
40270     /**
40271      * Returns the toolbar for this Panel if one was configured. 
40272      * @return {Roo.Toolbar} 
40273      */
40274     getToolbar : function(){
40275         return this.toolbar;
40276     },
40277     
40278     setActiveState : function(active)
40279     {
40280         this.active = active;
40281         this.setActiveClass(active);
40282         if(!active){
40283             if(this.fireEvent("deactivate", this) === false){
40284                 return false;
40285             }
40286             return true;
40287         }
40288         this.fireEvent("activate", this);
40289         return true;
40290     },
40291     /**
40292      * Updates this panel's element (not for iframe)
40293      * @param {String} content The new content
40294      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40295     */
40296     setContent : function(content, loadScripts){
40297         if (this.iframe) {
40298             return;
40299         }
40300         
40301         this.el.update(content, loadScripts);
40302     },
40303
40304     ignoreResize : function(w, h){
40305         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40306             return true;
40307         }else{
40308             this.lastSize = {width: w, height: h};
40309             return false;
40310         }
40311     },
40312     /**
40313      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40314      * @return {Roo.UpdateManager} The UpdateManager
40315      */
40316     getUpdateManager : function(){
40317         if (this.iframe) {
40318             return false;
40319         }
40320         return this.el.getUpdateManager();
40321     },
40322      /**
40323      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40324      * Does not work with IFRAME contents
40325      * @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:
40326 <pre><code>
40327 panel.load({
40328     url: "your-url.php",
40329     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40330     callback: yourFunction,
40331     scope: yourObject, //(optional scope)
40332     discardUrl: false,
40333     nocache: false,
40334     text: "Loading...",
40335     timeout: 30,
40336     scripts: false
40337 });
40338 </code></pre>
40339      
40340      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40341      * 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.
40342      * @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}
40343      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40344      * @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.
40345      * @return {Roo.ContentPanel} this
40346      */
40347     load : function(){
40348         
40349         if (this.iframe) {
40350             return this;
40351         }
40352         
40353         var um = this.el.getUpdateManager();
40354         um.update.apply(um, arguments);
40355         return this;
40356     },
40357
40358
40359     /**
40360      * 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.
40361      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40362      * @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)
40363      * @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)
40364      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40365      */
40366     setUrl : function(url, params, loadOnce){
40367         if (this.iframe) {
40368             this.iframeEl.dom.src = url;
40369             return false;
40370         }
40371         
40372         if(this.refreshDelegate){
40373             this.removeListener("activate", this.refreshDelegate);
40374         }
40375         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40376         this.on("activate", this.refreshDelegate);
40377         return this.el.getUpdateManager();
40378     },
40379     
40380     _handleRefresh : function(url, params, loadOnce){
40381         if(!loadOnce || !this.loaded){
40382             var updater = this.el.getUpdateManager();
40383             updater.update(url, params, this._setLoaded.createDelegate(this));
40384         }
40385     },
40386     
40387     _setLoaded : function(){
40388         this.loaded = true;
40389     }, 
40390     
40391     /**
40392      * Returns this panel's id
40393      * @return {String} 
40394      */
40395     getId : function(){
40396         return this.el.id;
40397     },
40398     
40399     /** 
40400      * Returns this panel's element - used by regiosn to add.
40401      * @return {Roo.Element} 
40402      */
40403     getEl : function(){
40404         return this.wrapEl || this.el;
40405     },
40406     
40407    
40408     
40409     adjustForComponents : function(width, height)
40410     {
40411         //Roo.log('adjustForComponents ');
40412         if(this.resizeEl != this.el){
40413             width -= this.el.getFrameWidth('lr');
40414             height -= this.el.getFrameWidth('tb');
40415         }
40416         if(this.toolbar){
40417             var te = this.toolbar.getEl();
40418             te.setWidth(width);
40419             height -= te.getHeight();
40420         }
40421         if(this.footer){
40422             var te = this.footer.getEl();
40423             te.setWidth(width);
40424             height -= te.getHeight();
40425         }
40426         
40427         
40428         if(this.adjustments){
40429             width += this.adjustments[0];
40430             height += this.adjustments[1];
40431         }
40432         return {"width": width, "height": height};
40433     },
40434     
40435     setSize : function(width, height){
40436         if(this.fitToFrame && !this.ignoreResize(width, height)){
40437             if(this.fitContainer && this.resizeEl != this.el){
40438                 this.el.setSize(width, height);
40439             }
40440             var size = this.adjustForComponents(width, height);
40441             if (this.iframe) {
40442                 this.iframeEl.setSize(width,height);
40443             }
40444             
40445             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40446             this.fireEvent('resize', this, size.width, size.height);
40447             
40448             
40449         }
40450     },
40451     
40452     /**
40453      * Returns this panel's title
40454      * @return {String} 
40455      */
40456     getTitle : function(){
40457         
40458         if (typeof(this.title) != 'object') {
40459             return this.title;
40460         }
40461         
40462         var t = '';
40463         for (var k in this.title) {
40464             if (!this.title.hasOwnProperty(k)) {
40465                 continue;
40466             }
40467             
40468             if (k.indexOf('-') >= 0) {
40469                 var s = k.split('-');
40470                 for (var i = 0; i<s.length; i++) {
40471                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40472                 }
40473             } else {
40474                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40475             }
40476         }
40477         return t;
40478     },
40479     
40480     /**
40481      * Set this panel's title
40482      * @param {String} title
40483      */
40484     setTitle : function(title){
40485         this.title = title;
40486         if(this.region){
40487             this.region.updatePanelTitle(this, title);
40488         }
40489     },
40490     
40491     /**
40492      * Returns true is this panel was configured to be closable
40493      * @return {Boolean} 
40494      */
40495     isClosable : function(){
40496         return this.closable;
40497     },
40498     
40499     beforeSlide : function(){
40500         this.el.clip();
40501         this.resizeEl.clip();
40502     },
40503     
40504     afterSlide : function(){
40505         this.el.unclip();
40506         this.resizeEl.unclip();
40507     },
40508     
40509     /**
40510      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40511      *   Will fail silently if the {@link #setUrl} method has not been called.
40512      *   This does not activate the panel, just updates its content.
40513      */
40514     refresh : function(){
40515         if(this.refreshDelegate){
40516            this.loaded = false;
40517            this.refreshDelegate();
40518         }
40519     },
40520     
40521     /**
40522      * Destroys this panel
40523      */
40524     destroy : function(){
40525         this.el.removeAllListeners();
40526         var tempEl = document.createElement("span");
40527         tempEl.appendChild(this.el.dom);
40528         tempEl.innerHTML = "";
40529         this.el.remove();
40530         this.el = null;
40531     },
40532     
40533     /**
40534      * form - if the content panel contains a form - this is a reference to it.
40535      * @type {Roo.form.Form}
40536      */
40537     form : false,
40538     /**
40539      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40540      *    This contains a reference to it.
40541      * @type {Roo.View}
40542      */
40543     view : false,
40544     
40545       /**
40546      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40547      * <pre><code>
40548
40549 layout.addxtype({
40550        xtype : 'Form',
40551        items: [ .... ]
40552    }
40553 );
40554
40555 </code></pre>
40556      * @param {Object} cfg Xtype definition of item to add.
40557      */
40558     
40559     
40560     getChildContainer: function () {
40561         return this.getEl();
40562     }
40563     
40564     
40565     /*
40566         var  ret = new Roo.factory(cfg);
40567         return ret;
40568         
40569         
40570         // add form..
40571         if (cfg.xtype.match(/^Form$/)) {
40572             
40573             var el;
40574             //if (this.footer) {
40575             //    el = this.footer.container.insertSibling(false, 'before');
40576             //} else {
40577                 el = this.el.createChild();
40578             //}
40579
40580             this.form = new  Roo.form.Form(cfg);
40581             
40582             
40583             if ( this.form.allItems.length) {
40584                 this.form.render(el.dom);
40585             }
40586             return this.form;
40587         }
40588         // should only have one of theses..
40589         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40590             // views.. should not be just added - used named prop 'view''
40591             
40592             cfg.el = this.el.appendChild(document.createElement("div"));
40593             // factory?
40594             
40595             var ret = new Roo.factory(cfg);
40596              
40597              ret.render && ret.render(false, ''); // render blank..
40598             this.view = ret;
40599             return ret;
40600         }
40601         return false;
40602     }
40603     \*/
40604 });
40605  
40606 /**
40607  * @class Roo.bootstrap.panel.Grid
40608  * @extends Roo.bootstrap.panel.Content
40609  * @constructor
40610  * Create a new GridPanel.
40611  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40612  * @param {Object} config A the config object
40613   
40614  */
40615
40616
40617
40618 Roo.bootstrap.panel.Grid = function(config)
40619 {
40620     
40621       
40622     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40623         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40624
40625     config.el = this.wrapper;
40626     //this.el = this.wrapper;
40627     
40628       if (config.container) {
40629         // ctor'ed from a Border/panel.grid
40630         
40631         
40632         this.wrapper.setStyle("overflow", "hidden");
40633         this.wrapper.addClass('roo-grid-container');
40634
40635     }
40636     
40637     
40638     if(config.toolbar){
40639         var tool_el = this.wrapper.createChild();    
40640         this.toolbar = Roo.factory(config.toolbar);
40641         var ti = [];
40642         if (config.toolbar.items) {
40643             ti = config.toolbar.items ;
40644             delete config.toolbar.items ;
40645         }
40646         
40647         var nitems = [];
40648         this.toolbar.render(tool_el);
40649         for(var i =0;i < ti.length;i++) {
40650           //  Roo.log(['add child', items[i]]);
40651             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40652         }
40653         this.toolbar.items = nitems;
40654         
40655         delete config.toolbar;
40656     }
40657     
40658     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40659     config.grid.scrollBody = true;;
40660     config.grid.monitorWindowResize = false; // turn off autosizing
40661     config.grid.autoHeight = false;
40662     config.grid.autoWidth = false;
40663     
40664     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40665     
40666     if (config.background) {
40667         // render grid on panel activation (if panel background)
40668         this.on('activate', function(gp) {
40669             if (!gp.grid.rendered) {
40670                 gp.grid.render(this.wrapper);
40671                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40672             }
40673         });
40674             
40675     } else {
40676         this.grid.render(this.wrapper);
40677         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40678
40679     }
40680     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40681     // ??? needed ??? config.el = this.wrapper;
40682     
40683     
40684     
40685   
40686     // xtype created footer. - not sure if will work as we normally have to render first..
40687     if (this.footer && !this.footer.el && this.footer.xtype) {
40688         
40689         var ctr = this.grid.getView().getFooterPanel(true);
40690         this.footer.dataSource = this.grid.dataSource;
40691         this.footer = Roo.factory(this.footer, Roo);
40692         this.footer.render(ctr);
40693         
40694     }
40695     
40696     
40697     
40698     
40699      
40700 };
40701
40702 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40703     getId : function(){
40704         return this.grid.id;
40705     },
40706     
40707     /**
40708      * Returns the grid for this panel
40709      * @return {Roo.bootstrap.Table} 
40710      */
40711     getGrid : function(){
40712         return this.grid;    
40713     },
40714     
40715     setSize : function(width, height){
40716         if(!this.ignoreResize(width, height)){
40717             var grid = this.grid;
40718             var size = this.adjustForComponents(width, height);
40719             // tfoot is not a footer?
40720           
40721             
40722             var gridel = grid.getGridEl();
40723             gridel.setSize(size.width, size.height);
40724             
40725             var tbd = grid.getGridEl().select('tbody', true).first();
40726             var thd = grid.getGridEl().select('thead',true).first();
40727             var tbf= grid.getGridEl().select('tfoot', true).first();
40728
40729             if (tbf) {
40730                 size.height -= tbf.getHeight();
40731             }
40732             if (thd) {
40733                 size.height -= thd.getHeight();
40734             }
40735             
40736             tbd.setSize(size.width, size.height );
40737             // this is for the account management tab -seems to work there.
40738             var thd = grid.getGridEl().select('thead',true).first();
40739             //if (tbd) {
40740             //    tbd.setSize(size.width, size.height - thd.getHeight());
40741             //}
40742              
40743             grid.autoSize();
40744         }
40745     },
40746      
40747     
40748     
40749     beforeSlide : function(){
40750         this.grid.getView().scroller.clip();
40751     },
40752     
40753     afterSlide : function(){
40754         this.grid.getView().scroller.unclip();
40755     },
40756     
40757     destroy : function(){
40758         this.grid.destroy();
40759         delete this.grid;
40760         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40761     }
40762 });
40763
40764 /**
40765  * @class Roo.bootstrap.panel.Nest
40766  * @extends Roo.bootstrap.panel.Content
40767  * @constructor
40768  * Create a new Panel, that can contain a layout.Border.
40769  * 
40770  * 
40771  * @param {Roo.BorderLayout} layout The layout for this panel
40772  * @param {String/Object} config A string to set only the title or a config object
40773  */
40774 Roo.bootstrap.panel.Nest = function(config)
40775 {
40776     // construct with only one argument..
40777     /* FIXME - implement nicer consturctors
40778     if (layout.layout) {
40779         config = layout;
40780         layout = config.layout;
40781         delete config.layout;
40782     }
40783     if (layout.xtype && !layout.getEl) {
40784         // then layout needs constructing..
40785         layout = Roo.factory(layout, Roo);
40786     }
40787     */
40788     
40789     config.el =  config.layout.getEl();
40790     
40791     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40792     
40793     config.layout.monitorWindowResize = false; // turn off autosizing
40794     this.layout = config.layout;
40795     this.layout.getEl().addClass("roo-layout-nested-layout");
40796     this.layout.parent = this;
40797     
40798     
40799     
40800     
40801 };
40802
40803 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40804
40805     setSize : function(width, height){
40806         if(!this.ignoreResize(width, height)){
40807             var size = this.adjustForComponents(width, height);
40808             var el = this.layout.getEl();
40809             if (size.height < 1) {
40810                 el.setWidth(size.width);   
40811             } else {
40812                 el.setSize(size.width, size.height);
40813             }
40814             var touch = el.dom.offsetWidth;
40815             this.layout.layout();
40816             // ie requires a double layout on the first pass
40817             if(Roo.isIE && !this.initialized){
40818                 this.initialized = true;
40819                 this.layout.layout();
40820             }
40821         }
40822     },
40823     
40824     // activate all subpanels if not currently active..
40825     
40826     setActiveState : function(active){
40827         this.active = active;
40828         this.setActiveClass(active);
40829         
40830         if(!active){
40831             this.fireEvent("deactivate", this);
40832             return;
40833         }
40834         
40835         this.fireEvent("activate", this);
40836         // not sure if this should happen before or after..
40837         if (!this.layout) {
40838             return; // should not happen..
40839         }
40840         var reg = false;
40841         for (var r in this.layout.regions) {
40842             reg = this.layout.getRegion(r);
40843             if (reg.getActivePanel()) {
40844                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40845                 reg.setActivePanel(reg.getActivePanel());
40846                 continue;
40847             }
40848             if (!reg.panels.length) {
40849                 continue;
40850             }
40851             reg.showPanel(reg.getPanel(0));
40852         }
40853         
40854         
40855         
40856         
40857     },
40858     
40859     /**
40860      * Returns the nested BorderLayout for this panel
40861      * @return {Roo.BorderLayout} 
40862      */
40863     getLayout : function(){
40864         return this.layout;
40865     },
40866     
40867      /**
40868      * Adds a xtype elements to the layout of the nested panel
40869      * <pre><code>
40870
40871 panel.addxtype({
40872        xtype : 'ContentPanel',
40873        region: 'west',
40874        items: [ .... ]
40875    }
40876 );
40877
40878 panel.addxtype({
40879         xtype : 'NestedLayoutPanel',
40880         region: 'west',
40881         layout: {
40882            center: { },
40883            west: { }   
40884         },
40885         items : [ ... list of content panels or nested layout panels.. ]
40886    }
40887 );
40888 </code></pre>
40889      * @param {Object} cfg Xtype definition of item to add.
40890      */
40891     addxtype : function(cfg) {
40892         return this.layout.addxtype(cfg);
40893     
40894     }
40895 });/*
40896  * Based on:
40897  * Ext JS Library 1.1.1
40898  * Copyright(c) 2006-2007, Ext JS, LLC.
40899  *
40900  * Originally Released Under LGPL - original licence link has changed is not relivant.
40901  *
40902  * Fork - LGPL
40903  * <script type="text/javascript">
40904  */
40905 /**
40906  * @class Roo.TabPanel
40907  * @extends Roo.util.Observable
40908  * A lightweight tab container.
40909  * <br><br>
40910  * Usage:
40911  * <pre><code>
40912 // basic tabs 1, built from existing content
40913 var tabs = new Roo.TabPanel("tabs1");
40914 tabs.addTab("script", "View Script");
40915 tabs.addTab("markup", "View Markup");
40916 tabs.activate("script");
40917
40918 // more advanced tabs, built from javascript
40919 var jtabs = new Roo.TabPanel("jtabs");
40920 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40921
40922 // set up the UpdateManager
40923 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40924 var updater = tab2.getUpdateManager();
40925 updater.setDefaultUrl("ajax1.htm");
40926 tab2.on('activate', updater.refresh, updater, true);
40927
40928 // Use setUrl for Ajax loading
40929 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40930 tab3.setUrl("ajax2.htm", null, true);
40931
40932 // Disabled tab
40933 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40934 tab4.disable();
40935
40936 jtabs.activate("jtabs-1");
40937  * </code></pre>
40938  * @constructor
40939  * Create a new TabPanel.
40940  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40941  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40942  */
40943 Roo.bootstrap.panel.Tabs = function(config){
40944     /**
40945     * The container element for this TabPanel.
40946     * @type Roo.Element
40947     */
40948     this.el = Roo.get(config.el);
40949     delete config.el;
40950     if(config){
40951         if(typeof config == "boolean"){
40952             this.tabPosition = config ? "bottom" : "top";
40953         }else{
40954             Roo.apply(this, config);
40955         }
40956     }
40957     
40958     if(this.tabPosition == "bottom"){
40959         // if tabs are at the bottom = create the body first.
40960         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40961         this.el.addClass("roo-tabs-bottom");
40962     }
40963     // next create the tabs holders
40964     
40965     if (this.tabPosition == "west"){
40966         
40967         var reg = this.region; // fake it..
40968         while (reg) {
40969             if (!reg.mgr.parent) {
40970                 break;
40971             }
40972             reg = reg.mgr.parent.region;
40973         }
40974         Roo.log("got nest?");
40975         Roo.log(reg);
40976         if (reg.mgr.getRegion('west')) {
40977             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40978             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40979             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40980             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40981             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40982         
40983             
40984         }
40985         
40986         
40987     } else {
40988      
40989         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40990         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40991         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40992         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40993     }
40994     
40995     
40996     if(Roo.isIE){
40997         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40998     }
40999     
41000     // finally - if tabs are at the top, then create the body last..
41001     if(this.tabPosition != "bottom"){
41002         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41003          * @type Roo.Element
41004          */
41005         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41006         this.el.addClass("roo-tabs-top");
41007     }
41008     this.items = [];
41009
41010     this.bodyEl.setStyle("position", "relative");
41011
41012     this.active = null;
41013     this.activateDelegate = this.activate.createDelegate(this);
41014
41015     this.addEvents({
41016         /**
41017          * @event tabchange
41018          * Fires when the active tab changes
41019          * @param {Roo.TabPanel} this
41020          * @param {Roo.TabPanelItem} activePanel The new active tab
41021          */
41022         "tabchange": true,
41023         /**
41024          * @event beforetabchange
41025          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41026          * @param {Roo.TabPanel} this
41027          * @param {Object} e Set cancel to true on this object to cancel the tab change
41028          * @param {Roo.TabPanelItem} tab The tab being changed to
41029          */
41030         "beforetabchange" : true
41031     });
41032
41033     Roo.EventManager.onWindowResize(this.onResize, this);
41034     this.cpad = this.el.getPadding("lr");
41035     this.hiddenCount = 0;
41036
41037
41038     // toolbar on the tabbar support...
41039     if (this.toolbar) {
41040         alert("no toolbar support yet");
41041         this.toolbar  = false;
41042         /*
41043         var tcfg = this.toolbar;
41044         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41045         this.toolbar = new Roo.Toolbar(tcfg);
41046         if (Roo.isSafari) {
41047             var tbl = tcfg.container.child('table', true);
41048             tbl.setAttribute('width', '100%');
41049         }
41050         */
41051         
41052     }
41053    
41054
41055
41056     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41057 };
41058
41059 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41060     /*
41061      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41062      */
41063     tabPosition : "top",
41064     /*
41065      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41066      */
41067     currentTabWidth : 0,
41068     /*
41069      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41070      */
41071     minTabWidth : 40,
41072     /*
41073      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41074      */
41075     maxTabWidth : 250,
41076     /*
41077      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41078      */
41079     preferredTabWidth : 175,
41080     /*
41081      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41082      */
41083     resizeTabs : false,
41084     /*
41085      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41086      */
41087     monitorResize : true,
41088     /*
41089      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41090      */
41091     toolbar : false,  // set by caller..
41092     
41093     region : false, /// set by caller
41094     
41095     disableTooltips : true, // not used yet...
41096
41097     /**
41098      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41099      * @param {String} id The id of the div to use <b>or create</b>
41100      * @param {String} text The text for the tab
41101      * @param {String} content (optional) Content to put in the TabPanelItem body
41102      * @param {Boolean} closable (optional) True to create a close icon on the tab
41103      * @return {Roo.TabPanelItem} The created TabPanelItem
41104      */
41105     addTab : function(id, text, content, closable, tpl)
41106     {
41107         var item = new Roo.bootstrap.panel.TabItem({
41108             panel: this,
41109             id : id,
41110             text : text,
41111             closable : closable,
41112             tpl : tpl
41113         });
41114         this.addTabItem(item);
41115         if(content){
41116             item.setContent(content);
41117         }
41118         return item;
41119     },
41120
41121     /**
41122      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41123      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41124      * @return {Roo.TabPanelItem}
41125      */
41126     getTab : function(id){
41127         return this.items[id];
41128     },
41129
41130     /**
41131      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41132      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41133      */
41134     hideTab : function(id){
41135         var t = this.items[id];
41136         if(!t.isHidden()){
41137            t.setHidden(true);
41138            this.hiddenCount++;
41139            this.autoSizeTabs();
41140         }
41141     },
41142
41143     /**
41144      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41145      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41146      */
41147     unhideTab : function(id){
41148         var t = this.items[id];
41149         if(t.isHidden()){
41150            t.setHidden(false);
41151            this.hiddenCount--;
41152            this.autoSizeTabs();
41153         }
41154     },
41155
41156     /**
41157      * Adds an existing {@link Roo.TabPanelItem}.
41158      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41159      */
41160     addTabItem : function(item)
41161     {
41162         this.items[item.id] = item;
41163         this.items.push(item);
41164         this.autoSizeTabs();
41165       //  if(this.resizeTabs){
41166     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41167   //         this.autoSizeTabs();
41168 //        }else{
41169 //            item.autoSize();
41170        // }
41171     },
41172
41173     /**
41174      * Removes a {@link Roo.TabPanelItem}.
41175      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41176      */
41177     removeTab : function(id){
41178         var items = this.items;
41179         var tab = items[id];
41180         if(!tab) { return; }
41181         var index = items.indexOf(tab);
41182         if(this.active == tab && items.length > 1){
41183             var newTab = this.getNextAvailable(index);
41184             if(newTab) {
41185                 newTab.activate();
41186             }
41187         }
41188         this.stripEl.dom.removeChild(tab.pnode.dom);
41189         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41190             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41191         }
41192         items.splice(index, 1);
41193         delete this.items[tab.id];
41194         tab.fireEvent("close", tab);
41195         tab.purgeListeners();
41196         this.autoSizeTabs();
41197     },
41198
41199     getNextAvailable : function(start){
41200         var items = this.items;
41201         var index = start;
41202         // look for a next tab that will slide over to
41203         // replace the one being removed
41204         while(index < items.length){
41205             var item = items[++index];
41206             if(item && !item.isHidden()){
41207                 return item;
41208             }
41209         }
41210         // if one isn't found select the previous tab (on the left)
41211         index = start;
41212         while(index >= 0){
41213             var item = items[--index];
41214             if(item && !item.isHidden()){
41215                 return item;
41216             }
41217         }
41218         return null;
41219     },
41220
41221     /**
41222      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41223      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41224      */
41225     disableTab : function(id){
41226         var tab = this.items[id];
41227         if(tab && this.active != tab){
41228             tab.disable();
41229         }
41230     },
41231
41232     /**
41233      * Enables a {@link Roo.TabPanelItem} that is disabled.
41234      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41235      */
41236     enableTab : function(id){
41237         var tab = this.items[id];
41238         tab.enable();
41239     },
41240
41241     /**
41242      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41243      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41244      * @return {Roo.TabPanelItem} The TabPanelItem.
41245      */
41246     activate : function(id)
41247     {
41248         //Roo.log('activite:'  + id);
41249         
41250         var tab = this.items[id];
41251         if(!tab){
41252             return null;
41253         }
41254         if(tab == this.active || tab.disabled){
41255             return tab;
41256         }
41257         var e = {};
41258         this.fireEvent("beforetabchange", this, e, tab);
41259         if(e.cancel !== true && !tab.disabled){
41260             if(this.active){
41261                 this.active.hide();
41262             }
41263             this.active = this.items[id];
41264             this.active.show();
41265             this.fireEvent("tabchange", this, this.active);
41266         }
41267         return tab;
41268     },
41269
41270     /**
41271      * Gets the active {@link Roo.TabPanelItem}.
41272      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41273      */
41274     getActiveTab : function(){
41275         return this.active;
41276     },
41277
41278     /**
41279      * Updates the tab body element to fit the height of the container element
41280      * for overflow scrolling
41281      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41282      */
41283     syncHeight : function(targetHeight){
41284         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41285         var bm = this.bodyEl.getMargins();
41286         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41287         this.bodyEl.setHeight(newHeight);
41288         return newHeight;
41289     },
41290
41291     onResize : function(){
41292         if(this.monitorResize){
41293             this.autoSizeTabs();
41294         }
41295     },
41296
41297     /**
41298      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41299      */
41300     beginUpdate : function(){
41301         this.updating = true;
41302     },
41303
41304     /**
41305      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41306      */
41307     endUpdate : function(){
41308         this.updating = false;
41309         this.autoSizeTabs();
41310     },
41311
41312     /**
41313      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41314      */
41315     autoSizeTabs : function()
41316     {
41317         var count = this.items.length;
41318         var vcount = count - this.hiddenCount;
41319         
41320         if (vcount < 2) {
41321             this.stripEl.hide();
41322         } else {
41323             this.stripEl.show();
41324         }
41325         
41326         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41327             return;
41328         }
41329         
41330         
41331         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41332         var availWidth = Math.floor(w / vcount);
41333         var b = this.stripBody;
41334         if(b.getWidth() > w){
41335             var tabs = this.items;
41336             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41337             if(availWidth < this.minTabWidth){
41338                 /*if(!this.sleft){    // incomplete scrolling code
41339                     this.createScrollButtons();
41340                 }
41341                 this.showScroll();
41342                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41343             }
41344         }else{
41345             if(this.currentTabWidth < this.preferredTabWidth){
41346                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41347             }
41348         }
41349     },
41350
41351     /**
41352      * Returns the number of tabs in this TabPanel.
41353      * @return {Number}
41354      */
41355      getCount : function(){
41356          return this.items.length;
41357      },
41358
41359     /**
41360      * Resizes all the tabs to the passed width
41361      * @param {Number} The new width
41362      */
41363     setTabWidth : function(width){
41364         this.currentTabWidth = width;
41365         for(var i = 0, len = this.items.length; i < len; i++) {
41366                 if(!this.items[i].isHidden()) {
41367                 this.items[i].setWidth(width);
41368             }
41369         }
41370     },
41371
41372     /**
41373      * Destroys this TabPanel
41374      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41375      */
41376     destroy : function(removeEl){
41377         Roo.EventManager.removeResizeListener(this.onResize, this);
41378         for(var i = 0, len = this.items.length; i < len; i++){
41379             this.items[i].purgeListeners();
41380         }
41381         if(removeEl === true){
41382             this.el.update("");
41383             this.el.remove();
41384         }
41385     },
41386     
41387     createStrip : function(container)
41388     {
41389         var strip = document.createElement("nav");
41390         strip.className = Roo.bootstrap.version == 4 ?
41391             "navbar-light bg-light" : 
41392             "navbar navbar-default"; //"x-tabs-wrap";
41393         container.appendChild(strip);
41394         return strip;
41395     },
41396     
41397     createStripList : function(strip)
41398     {
41399         // div wrapper for retard IE
41400         // returns the "tr" element.
41401         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41402         //'<div class="x-tabs-strip-wrap">'+
41403           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41404           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41405         return strip.firstChild; //.firstChild.firstChild.firstChild;
41406     },
41407     createBody : function(container)
41408     {
41409         var body = document.createElement("div");
41410         Roo.id(body, "tab-body");
41411         //Roo.fly(body).addClass("x-tabs-body");
41412         Roo.fly(body).addClass("tab-content");
41413         container.appendChild(body);
41414         return body;
41415     },
41416     createItemBody :function(bodyEl, id){
41417         var body = Roo.getDom(id);
41418         if(!body){
41419             body = document.createElement("div");
41420             body.id = id;
41421         }
41422         //Roo.fly(body).addClass("x-tabs-item-body");
41423         Roo.fly(body).addClass("tab-pane");
41424          bodyEl.insertBefore(body, bodyEl.firstChild);
41425         return body;
41426     },
41427     /** @private */
41428     createStripElements :  function(stripEl, text, closable, tpl)
41429     {
41430         var td = document.createElement("li"); // was td..
41431         td.className = 'nav-item';
41432         
41433         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41434         
41435         
41436         stripEl.appendChild(td);
41437         /*if(closable){
41438             td.className = "x-tabs-closable";
41439             if(!this.closeTpl){
41440                 this.closeTpl = new Roo.Template(
41441                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41442                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41443                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41444                 );
41445             }
41446             var el = this.closeTpl.overwrite(td, {"text": text});
41447             var close = el.getElementsByTagName("div")[0];
41448             var inner = el.getElementsByTagName("em")[0];
41449             return {"el": el, "close": close, "inner": inner};
41450         } else {
41451         */
41452         // not sure what this is..
41453 //            if(!this.tabTpl){
41454                 //this.tabTpl = new Roo.Template(
41455                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41456                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41457                 //);
41458 //                this.tabTpl = new Roo.Template(
41459 //                   '<a href="#">' +
41460 //                   '<span unselectable="on"' +
41461 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41462 //                            ' >{text}</span></a>'
41463 //                );
41464 //                
41465 //            }
41466
41467
41468             var template = tpl || this.tabTpl || false;
41469             
41470             if(!template){
41471                 template =  new Roo.Template(
41472                         Roo.bootstrap.version == 4 ? 
41473                             (
41474                                 '<a class="nav-link" href="#" unselectable="on"' +
41475                                      (this.disableTooltips ? '' : ' title="{text}"') +
41476                                      ' >{text}</a>'
41477                             ) : (
41478                                 '<a class="nav-link" href="#">' +
41479                                 '<span unselectable="on"' +
41480                                          (this.disableTooltips ? '' : ' title="{text}"') +
41481                                     ' >{text}</span></a>'
41482                             )
41483                 );
41484             }
41485             
41486             switch (typeof(template)) {
41487                 case 'object' :
41488                     break;
41489                 case 'string' :
41490                     template = new Roo.Template(template);
41491                     break;
41492                 default :
41493                     break;
41494             }
41495             
41496             var el = template.overwrite(td, {"text": text});
41497             
41498             var inner = el.getElementsByTagName("span")[0];
41499             
41500             return {"el": el, "inner": inner};
41501             
41502     }
41503         
41504     
41505 });
41506
41507 /**
41508  * @class Roo.TabPanelItem
41509  * @extends Roo.util.Observable
41510  * Represents an individual item (tab plus body) in a TabPanel.
41511  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41512  * @param {String} id The id of this TabPanelItem
41513  * @param {String} text The text for the tab of this TabPanelItem
41514  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41515  */
41516 Roo.bootstrap.panel.TabItem = function(config){
41517     /**
41518      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41519      * @type Roo.TabPanel
41520      */
41521     this.tabPanel = config.panel;
41522     /**
41523      * The id for this TabPanelItem
41524      * @type String
41525      */
41526     this.id = config.id;
41527     /** @private */
41528     this.disabled = false;
41529     /** @private */
41530     this.text = config.text;
41531     /** @private */
41532     this.loaded = false;
41533     this.closable = config.closable;
41534
41535     /**
41536      * The body element for this TabPanelItem.
41537      * @type Roo.Element
41538      */
41539     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41540     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41541     this.bodyEl.setStyle("display", "block");
41542     this.bodyEl.setStyle("zoom", "1");
41543     //this.hideAction();
41544
41545     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41546     /** @private */
41547     this.el = Roo.get(els.el);
41548     this.inner = Roo.get(els.inner, true);
41549      this.textEl = Roo.bootstrap.version == 4 ?
41550         this.el : Roo.get(this.el.dom.firstChild, true);
41551
41552     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41553     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41554
41555     
41556 //    this.el.on("mousedown", this.onTabMouseDown, this);
41557     this.el.on("click", this.onTabClick, this);
41558     /** @private */
41559     if(config.closable){
41560         var c = Roo.get(els.close, true);
41561         c.dom.title = this.closeText;
41562         c.addClassOnOver("close-over");
41563         c.on("click", this.closeClick, this);
41564      }
41565
41566     this.addEvents({
41567          /**
41568          * @event activate
41569          * Fires when this tab becomes the active tab.
41570          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41571          * @param {Roo.TabPanelItem} this
41572          */
41573         "activate": true,
41574         /**
41575          * @event beforeclose
41576          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41577          * @param {Roo.TabPanelItem} this
41578          * @param {Object} e Set cancel to true on this object to cancel the close.
41579          */
41580         "beforeclose": true,
41581         /**
41582          * @event close
41583          * Fires when this tab is closed.
41584          * @param {Roo.TabPanelItem} this
41585          */
41586          "close": true,
41587         /**
41588          * @event deactivate
41589          * Fires when this tab is no longer the active tab.
41590          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41591          * @param {Roo.TabPanelItem} this
41592          */
41593          "deactivate" : true
41594     });
41595     this.hidden = false;
41596
41597     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41598 };
41599
41600 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41601            {
41602     purgeListeners : function(){
41603        Roo.util.Observable.prototype.purgeListeners.call(this);
41604        this.el.removeAllListeners();
41605     },
41606     /**
41607      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41608      */
41609     show : function(){
41610         this.status_node.addClass("active");
41611         this.showAction();
41612         if(Roo.isOpera){
41613             this.tabPanel.stripWrap.repaint();
41614         }
41615         this.fireEvent("activate", this.tabPanel, this);
41616     },
41617
41618     /**
41619      * Returns true if this tab is the active tab.
41620      * @return {Boolean}
41621      */
41622     isActive : function(){
41623         return this.tabPanel.getActiveTab() == this;
41624     },
41625
41626     /**
41627      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41628      */
41629     hide : function(){
41630         this.status_node.removeClass("active");
41631         this.hideAction();
41632         this.fireEvent("deactivate", this.tabPanel, this);
41633     },
41634
41635     hideAction : function(){
41636         this.bodyEl.hide();
41637         this.bodyEl.setStyle("position", "absolute");
41638         this.bodyEl.setLeft("-20000px");
41639         this.bodyEl.setTop("-20000px");
41640     },
41641
41642     showAction : function(){
41643         this.bodyEl.setStyle("position", "relative");
41644         this.bodyEl.setTop("");
41645         this.bodyEl.setLeft("");
41646         this.bodyEl.show();
41647     },
41648
41649     /**
41650      * Set the tooltip for the tab.
41651      * @param {String} tooltip The tab's tooltip
41652      */
41653     setTooltip : function(text){
41654         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41655             this.textEl.dom.qtip = text;
41656             this.textEl.dom.removeAttribute('title');
41657         }else{
41658             this.textEl.dom.title = text;
41659         }
41660     },
41661
41662     onTabClick : function(e){
41663         e.preventDefault();
41664         this.tabPanel.activate(this.id);
41665     },
41666
41667     onTabMouseDown : function(e){
41668         e.preventDefault();
41669         this.tabPanel.activate(this.id);
41670     },
41671 /*
41672     getWidth : function(){
41673         return this.inner.getWidth();
41674     },
41675
41676     setWidth : function(width){
41677         var iwidth = width - this.linode.getPadding("lr");
41678         this.inner.setWidth(iwidth);
41679         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41680         this.linode.setWidth(width);
41681     },
41682 */
41683     /**
41684      * Show or hide the tab
41685      * @param {Boolean} hidden True to hide or false to show.
41686      */
41687     setHidden : function(hidden){
41688         this.hidden = hidden;
41689         this.linode.setStyle("display", hidden ? "none" : "");
41690     },
41691
41692     /**
41693      * Returns true if this tab is "hidden"
41694      * @return {Boolean}
41695      */
41696     isHidden : function(){
41697         return this.hidden;
41698     },
41699
41700     /**
41701      * Returns the text for this tab
41702      * @return {String}
41703      */
41704     getText : function(){
41705         return this.text;
41706     },
41707     /*
41708     autoSize : function(){
41709         //this.el.beginMeasure();
41710         this.textEl.setWidth(1);
41711         /*
41712          *  #2804 [new] Tabs in Roojs
41713          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41714          */
41715         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41716         //this.el.endMeasure();
41717     //},
41718
41719     /**
41720      * Sets the text for the tab (Note: this also sets the tooltip text)
41721      * @param {String} text The tab's text and tooltip
41722      */
41723     setText : function(text){
41724         this.text = text;
41725         this.textEl.update(text);
41726         this.setTooltip(text);
41727         //if(!this.tabPanel.resizeTabs){
41728         //    this.autoSize();
41729         //}
41730     },
41731     /**
41732      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41733      */
41734     activate : function(){
41735         this.tabPanel.activate(this.id);
41736     },
41737
41738     /**
41739      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41740      */
41741     disable : function(){
41742         if(this.tabPanel.active != this){
41743             this.disabled = true;
41744             this.status_node.addClass("disabled");
41745         }
41746     },
41747
41748     /**
41749      * Enables this TabPanelItem if it was previously disabled.
41750      */
41751     enable : function(){
41752         this.disabled = false;
41753         this.status_node.removeClass("disabled");
41754     },
41755
41756     /**
41757      * Sets the content for this TabPanelItem.
41758      * @param {String} content The content
41759      * @param {Boolean} loadScripts true to look for and load scripts
41760      */
41761     setContent : function(content, loadScripts){
41762         this.bodyEl.update(content, loadScripts);
41763     },
41764
41765     /**
41766      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41767      * @return {Roo.UpdateManager} The UpdateManager
41768      */
41769     getUpdateManager : function(){
41770         return this.bodyEl.getUpdateManager();
41771     },
41772
41773     /**
41774      * Set a URL to be used to load the content for this TabPanelItem.
41775      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41776      * @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)
41777      * @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)
41778      * @return {Roo.UpdateManager} The UpdateManager
41779      */
41780     setUrl : function(url, params, loadOnce){
41781         if(this.refreshDelegate){
41782             this.un('activate', this.refreshDelegate);
41783         }
41784         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41785         this.on("activate", this.refreshDelegate);
41786         return this.bodyEl.getUpdateManager();
41787     },
41788
41789     /** @private */
41790     _handleRefresh : function(url, params, loadOnce){
41791         if(!loadOnce || !this.loaded){
41792             var updater = this.bodyEl.getUpdateManager();
41793             updater.update(url, params, this._setLoaded.createDelegate(this));
41794         }
41795     },
41796
41797     /**
41798      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41799      *   Will fail silently if the setUrl method has not been called.
41800      *   This does not activate the panel, just updates its content.
41801      */
41802     refresh : function(){
41803         if(this.refreshDelegate){
41804            this.loaded = false;
41805            this.refreshDelegate();
41806         }
41807     },
41808
41809     /** @private */
41810     _setLoaded : function(){
41811         this.loaded = true;
41812     },
41813
41814     /** @private */
41815     closeClick : function(e){
41816         var o = {};
41817         e.stopEvent();
41818         this.fireEvent("beforeclose", this, o);
41819         if(o.cancel !== true){
41820             this.tabPanel.removeTab(this.id);
41821         }
41822     },
41823     /**
41824      * The text displayed in the tooltip for the close icon.
41825      * @type String
41826      */
41827     closeText : "Close this tab"
41828 });
41829 /**
41830 *    This script refer to:
41831 *    Title: International Telephone Input
41832 *    Author: Jack O'Connor
41833 *    Code version:  v12.1.12
41834 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41835 **/
41836
41837 Roo.bootstrap.PhoneInputData = function() {
41838     var d = [
41839       [
41840         "Afghanistan (‫افغانستان‬‎)",
41841         "af",
41842         "93"
41843       ],
41844       [
41845         "Albania (Shqipëri)",
41846         "al",
41847         "355"
41848       ],
41849       [
41850         "Algeria (‫الجزائر‬‎)",
41851         "dz",
41852         "213"
41853       ],
41854       [
41855         "American Samoa",
41856         "as",
41857         "1684"
41858       ],
41859       [
41860         "Andorra",
41861         "ad",
41862         "376"
41863       ],
41864       [
41865         "Angola",
41866         "ao",
41867         "244"
41868       ],
41869       [
41870         "Anguilla",
41871         "ai",
41872         "1264"
41873       ],
41874       [
41875         "Antigua and Barbuda",
41876         "ag",
41877         "1268"
41878       ],
41879       [
41880         "Argentina",
41881         "ar",
41882         "54"
41883       ],
41884       [
41885         "Armenia (Հայաստան)",
41886         "am",
41887         "374"
41888       ],
41889       [
41890         "Aruba",
41891         "aw",
41892         "297"
41893       ],
41894       [
41895         "Australia",
41896         "au",
41897         "61",
41898         0
41899       ],
41900       [
41901         "Austria (Österreich)",
41902         "at",
41903         "43"
41904       ],
41905       [
41906         "Azerbaijan (Azərbaycan)",
41907         "az",
41908         "994"
41909       ],
41910       [
41911         "Bahamas",
41912         "bs",
41913         "1242"
41914       ],
41915       [
41916         "Bahrain (‫البحرين‬‎)",
41917         "bh",
41918         "973"
41919       ],
41920       [
41921         "Bangladesh (বাংলাদেশ)",
41922         "bd",
41923         "880"
41924       ],
41925       [
41926         "Barbados",
41927         "bb",
41928         "1246"
41929       ],
41930       [
41931         "Belarus (Беларусь)",
41932         "by",
41933         "375"
41934       ],
41935       [
41936         "Belgium (België)",
41937         "be",
41938         "32"
41939       ],
41940       [
41941         "Belize",
41942         "bz",
41943         "501"
41944       ],
41945       [
41946         "Benin (Bénin)",
41947         "bj",
41948         "229"
41949       ],
41950       [
41951         "Bermuda",
41952         "bm",
41953         "1441"
41954       ],
41955       [
41956         "Bhutan (འབྲུག)",
41957         "bt",
41958         "975"
41959       ],
41960       [
41961         "Bolivia",
41962         "bo",
41963         "591"
41964       ],
41965       [
41966         "Bosnia and Herzegovina (Босна и Херцеговина)",
41967         "ba",
41968         "387"
41969       ],
41970       [
41971         "Botswana",
41972         "bw",
41973         "267"
41974       ],
41975       [
41976         "Brazil (Brasil)",
41977         "br",
41978         "55"
41979       ],
41980       [
41981         "British Indian Ocean Territory",
41982         "io",
41983         "246"
41984       ],
41985       [
41986         "British Virgin Islands",
41987         "vg",
41988         "1284"
41989       ],
41990       [
41991         "Brunei",
41992         "bn",
41993         "673"
41994       ],
41995       [
41996         "Bulgaria (България)",
41997         "bg",
41998         "359"
41999       ],
42000       [
42001         "Burkina Faso",
42002         "bf",
42003         "226"
42004       ],
42005       [
42006         "Burundi (Uburundi)",
42007         "bi",
42008         "257"
42009       ],
42010       [
42011         "Cambodia (កម្ពុជា)",
42012         "kh",
42013         "855"
42014       ],
42015       [
42016         "Cameroon (Cameroun)",
42017         "cm",
42018         "237"
42019       ],
42020       [
42021         "Canada",
42022         "ca",
42023         "1",
42024         1,
42025         ["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"]
42026       ],
42027       [
42028         "Cape Verde (Kabu Verdi)",
42029         "cv",
42030         "238"
42031       ],
42032       [
42033         "Caribbean Netherlands",
42034         "bq",
42035         "599",
42036         1
42037       ],
42038       [
42039         "Cayman Islands",
42040         "ky",
42041         "1345"
42042       ],
42043       [
42044         "Central African Republic (République centrafricaine)",
42045         "cf",
42046         "236"
42047       ],
42048       [
42049         "Chad (Tchad)",
42050         "td",
42051         "235"
42052       ],
42053       [
42054         "Chile",
42055         "cl",
42056         "56"
42057       ],
42058       [
42059         "China (中国)",
42060         "cn",
42061         "86"
42062       ],
42063       [
42064         "Christmas Island",
42065         "cx",
42066         "61",
42067         2
42068       ],
42069       [
42070         "Cocos (Keeling) Islands",
42071         "cc",
42072         "61",
42073         1
42074       ],
42075       [
42076         "Colombia",
42077         "co",
42078         "57"
42079       ],
42080       [
42081         "Comoros (‫جزر القمر‬‎)",
42082         "km",
42083         "269"
42084       ],
42085       [
42086         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42087         "cd",
42088         "243"
42089       ],
42090       [
42091         "Congo (Republic) (Congo-Brazzaville)",
42092         "cg",
42093         "242"
42094       ],
42095       [
42096         "Cook Islands",
42097         "ck",
42098         "682"
42099       ],
42100       [
42101         "Costa Rica",
42102         "cr",
42103         "506"
42104       ],
42105       [
42106         "Côte d’Ivoire",
42107         "ci",
42108         "225"
42109       ],
42110       [
42111         "Croatia (Hrvatska)",
42112         "hr",
42113         "385"
42114       ],
42115       [
42116         "Cuba",
42117         "cu",
42118         "53"
42119       ],
42120       [
42121         "Curaçao",
42122         "cw",
42123         "599",
42124         0
42125       ],
42126       [
42127         "Cyprus (Κύπρος)",
42128         "cy",
42129         "357"
42130       ],
42131       [
42132         "Czech Republic (Česká republika)",
42133         "cz",
42134         "420"
42135       ],
42136       [
42137         "Denmark (Danmark)",
42138         "dk",
42139         "45"
42140       ],
42141       [
42142         "Djibouti",
42143         "dj",
42144         "253"
42145       ],
42146       [
42147         "Dominica",
42148         "dm",
42149         "1767"
42150       ],
42151       [
42152         "Dominican Republic (República Dominicana)",
42153         "do",
42154         "1",
42155         2,
42156         ["809", "829", "849"]
42157       ],
42158       [
42159         "Ecuador",
42160         "ec",
42161         "593"
42162       ],
42163       [
42164         "Egypt (‫مصر‬‎)",
42165         "eg",
42166         "20"
42167       ],
42168       [
42169         "El Salvador",
42170         "sv",
42171         "503"
42172       ],
42173       [
42174         "Equatorial Guinea (Guinea Ecuatorial)",
42175         "gq",
42176         "240"
42177       ],
42178       [
42179         "Eritrea",
42180         "er",
42181         "291"
42182       ],
42183       [
42184         "Estonia (Eesti)",
42185         "ee",
42186         "372"
42187       ],
42188       [
42189         "Ethiopia",
42190         "et",
42191         "251"
42192       ],
42193       [
42194         "Falkland Islands (Islas Malvinas)",
42195         "fk",
42196         "500"
42197       ],
42198       [
42199         "Faroe Islands (Føroyar)",
42200         "fo",
42201         "298"
42202       ],
42203       [
42204         "Fiji",
42205         "fj",
42206         "679"
42207       ],
42208       [
42209         "Finland (Suomi)",
42210         "fi",
42211         "358",
42212         0
42213       ],
42214       [
42215         "France",
42216         "fr",
42217         "33"
42218       ],
42219       [
42220         "French Guiana (Guyane française)",
42221         "gf",
42222         "594"
42223       ],
42224       [
42225         "French Polynesia (Polynésie française)",
42226         "pf",
42227         "689"
42228       ],
42229       [
42230         "Gabon",
42231         "ga",
42232         "241"
42233       ],
42234       [
42235         "Gambia",
42236         "gm",
42237         "220"
42238       ],
42239       [
42240         "Georgia (საქართველო)",
42241         "ge",
42242         "995"
42243       ],
42244       [
42245         "Germany (Deutschland)",
42246         "de",
42247         "49"
42248       ],
42249       [
42250         "Ghana (Gaana)",
42251         "gh",
42252         "233"
42253       ],
42254       [
42255         "Gibraltar",
42256         "gi",
42257         "350"
42258       ],
42259       [
42260         "Greece (Ελλάδα)",
42261         "gr",
42262         "30"
42263       ],
42264       [
42265         "Greenland (Kalaallit Nunaat)",
42266         "gl",
42267         "299"
42268       ],
42269       [
42270         "Grenada",
42271         "gd",
42272         "1473"
42273       ],
42274       [
42275         "Guadeloupe",
42276         "gp",
42277         "590",
42278         0
42279       ],
42280       [
42281         "Guam",
42282         "gu",
42283         "1671"
42284       ],
42285       [
42286         "Guatemala",
42287         "gt",
42288         "502"
42289       ],
42290       [
42291         "Guernsey",
42292         "gg",
42293         "44",
42294         1
42295       ],
42296       [
42297         "Guinea (Guinée)",
42298         "gn",
42299         "224"
42300       ],
42301       [
42302         "Guinea-Bissau (Guiné Bissau)",
42303         "gw",
42304         "245"
42305       ],
42306       [
42307         "Guyana",
42308         "gy",
42309         "592"
42310       ],
42311       [
42312         "Haiti",
42313         "ht",
42314         "509"
42315       ],
42316       [
42317         "Honduras",
42318         "hn",
42319         "504"
42320       ],
42321       [
42322         "Hong Kong (香港)",
42323         "hk",
42324         "852"
42325       ],
42326       [
42327         "Hungary (Magyarország)",
42328         "hu",
42329         "36"
42330       ],
42331       [
42332         "Iceland (Ísland)",
42333         "is",
42334         "354"
42335       ],
42336       [
42337         "India (भारत)",
42338         "in",
42339         "91"
42340       ],
42341       [
42342         "Indonesia",
42343         "id",
42344         "62"
42345       ],
42346       [
42347         "Iran (‫ایران‬‎)",
42348         "ir",
42349         "98"
42350       ],
42351       [
42352         "Iraq (‫العراق‬‎)",
42353         "iq",
42354         "964"
42355       ],
42356       [
42357         "Ireland",
42358         "ie",
42359         "353"
42360       ],
42361       [
42362         "Isle of Man",
42363         "im",
42364         "44",
42365         2
42366       ],
42367       [
42368         "Israel (‫ישראל‬‎)",
42369         "il",
42370         "972"
42371       ],
42372       [
42373         "Italy (Italia)",
42374         "it",
42375         "39",
42376         0
42377       ],
42378       [
42379         "Jamaica",
42380         "jm",
42381         "1876"
42382       ],
42383       [
42384         "Japan (日本)",
42385         "jp",
42386         "81"
42387       ],
42388       [
42389         "Jersey",
42390         "je",
42391         "44",
42392         3
42393       ],
42394       [
42395         "Jordan (‫الأردن‬‎)",
42396         "jo",
42397         "962"
42398       ],
42399       [
42400         "Kazakhstan (Казахстан)",
42401         "kz",
42402         "7",
42403         1
42404       ],
42405       [
42406         "Kenya",
42407         "ke",
42408         "254"
42409       ],
42410       [
42411         "Kiribati",
42412         "ki",
42413         "686"
42414       ],
42415       [
42416         "Kosovo",
42417         "xk",
42418         "383"
42419       ],
42420       [
42421         "Kuwait (‫الكويت‬‎)",
42422         "kw",
42423         "965"
42424       ],
42425       [
42426         "Kyrgyzstan (Кыргызстан)",
42427         "kg",
42428         "996"
42429       ],
42430       [
42431         "Laos (ລາວ)",
42432         "la",
42433         "856"
42434       ],
42435       [
42436         "Latvia (Latvija)",
42437         "lv",
42438         "371"
42439       ],
42440       [
42441         "Lebanon (‫لبنان‬‎)",
42442         "lb",
42443         "961"
42444       ],
42445       [
42446         "Lesotho",
42447         "ls",
42448         "266"
42449       ],
42450       [
42451         "Liberia",
42452         "lr",
42453         "231"
42454       ],
42455       [
42456         "Libya (‫ليبيا‬‎)",
42457         "ly",
42458         "218"
42459       ],
42460       [
42461         "Liechtenstein",
42462         "li",
42463         "423"
42464       ],
42465       [
42466         "Lithuania (Lietuva)",
42467         "lt",
42468         "370"
42469       ],
42470       [
42471         "Luxembourg",
42472         "lu",
42473         "352"
42474       ],
42475       [
42476         "Macau (澳門)",
42477         "mo",
42478         "853"
42479       ],
42480       [
42481         "Macedonia (FYROM) (Македонија)",
42482         "mk",
42483         "389"
42484       ],
42485       [
42486         "Madagascar (Madagasikara)",
42487         "mg",
42488         "261"
42489       ],
42490       [
42491         "Malawi",
42492         "mw",
42493         "265"
42494       ],
42495       [
42496         "Malaysia",
42497         "my",
42498         "60"
42499       ],
42500       [
42501         "Maldives",
42502         "mv",
42503         "960"
42504       ],
42505       [
42506         "Mali",
42507         "ml",
42508         "223"
42509       ],
42510       [
42511         "Malta",
42512         "mt",
42513         "356"
42514       ],
42515       [
42516         "Marshall Islands",
42517         "mh",
42518         "692"
42519       ],
42520       [
42521         "Martinique",
42522         "mq",
42523         "596"
42524       ],
42525       [
42526         "Mauritania (‫موريتانيا‬‎)",
42527         "mr",
42528         "222"
42529       ],
42530       [
42531         "Mauritius (Moris)",
42532         "mu",
42533         "230"
42534       ],
42535       [
42536         "Mayotte",
42537         "yt",
42538         "262",
42539         1
42540       ],
42541       [
42542         "Mexico (México)",
42543         "mx",
42544         "52"
42545       ],
42546       [
42547         "Micronesia",
42548         "fm",
42549         "691"
42550       ],
42551       [
42552         "Moldova (Republica Moldova)",
42553         "md",
42554         "373"
42555       ],
42556       [
42557         "Monaco",
42558         "mc",
42559         "377"
42560       ],
42561       [
42562         "Mongolia (Монгол)",
42563         "mn",
42564         "976"
42565       ],
42566       [
42567         "Montenegro (Crna Gora)",
42568         "me",
42569         "382"
42570       ],
42571       [
42572         "Montserrat",
42573         "ms",
42574         "1664"
42575       ],
42576       [
42577         "Morocco (‫المغرب‬‎)",
42578         "ma",
42579         "212",
42580         0
42581       ],
42582       [
42583         "Mozambique (Moçambique)",
42584         "mz",
42585         "258"
42586       ],
42587       [
42588         "Myanmar (Burma) (မြန်မာ)",
42589         "mm",
42590         "95"
42591       ],
42592       [
42593         "Namibia (Namibië)",
42594         "na",
42595         "264"
42596       ],
42597       [
42598         "Nauru",
42599         "nr",
42600         "674"
42601       ],
42602       [
42603         "Nepal (नेपाल)",
42604         "np",
42605         "977"
42606       ],
42607       [
42608         "Netherlands (Nederland)",
42609         "nl",
42610         "31"
42611       ],
42612       [
42613         "New Caledonia (Nouvelle-Calédonie)",
42614         "nc",
42615         "687"
42616       ],
42617       [
42618         "New Zealand",
42619         "nz",
42620         "64"
42621       ],
42622       [
42623         "Nicaragua",
42624         "ni",
42625         "505"
42626       ],
42627       [
42628         "Niger (Nijar)",
42629         "ne",
42630         "227"
42631       ],
42632       [
42633         "Nigeria",
42634         "ng",
42635         "234"
42636       ],
42637       [
42638         "Niue",
42639         "nu",
42640         "683"
42641       ],
42642       [
42643         "Norfolk Island",
42644         "nf",
42645         "672"
42646       ],
42647       [
42648         "North Korea (조선 민주주의 인민 공화국)",
42649         "kp",
42650         "850"
42651       ],
42652       [
42653         "Northern Mariana Islands",
42654         "mp",
42655         "1670"
42656       ],
42657       [
42658         "Norway (Norge)",
42659         "no",
42660         "47",
42661         0
42662       ],
42663       [
42664         "Oman (‫عُمان‬‎)",
42665         "om",
42666         "968"
42667       ],
42668       [
42669         "Pakistan (‫پاکستان‬‎)",
42670         "pk",
42671         "92"
42672       ],
42673       [
42674         "Palau",
42675         "pw",
42676         "680"
42677       ],
42678       [
42679         "Palestine (‫فلسطين‬‎)",
42680         "ps",
42681         "970"
42682       ],
42683       [
42684         "Panama (Panamá)",
42685         "pa",
42686         "507"
42687       ],
42688       [
42689         "Papua New Guinea",
42690         "pg",
42691         "675"
42692       ],
42693       [
42694         "Paraguay",
42695         "py",
42696         "595"
42697       ],
42698       [
42699         "Peru (Perú)",
42700         "pe",
42701         "51"
42702       ],
42703       [
42704         "Philippines",
42705         "ph",
42706         "63"
42707       ],
42708       [
42709         "Poland (Polska)",
42710         "pl",
42711         "48"
42712       ],
42713       [
42714         "Portugal",
42715         "pt",
42716         "351"
42717       ],
42718       [
42719         "Puerto Rico",
42720         "pr",
42721         "1",
42722         3,
42723         ["787", "939"]
42724       ],
42725       [
42726         "Qatar (‫قطر‬‎)",
42727         "qa",
42728         "974"
42729       ],
42730       [
42731         "Réunion (La Réunion)",
42732         "re",
42733         "262",
42734         0
42735       ],
42736       [
42737         "Romania (România)",
42738         "ro",
42739         "40"
42740       ],
42741       [
42742         "Russia (Россия)",
42743         "ru",
42744         "7",
42745         0
42746       ],
42747       [
42748         "Rwanda",
42749         "rw",
42750         "250"
42751       ],
42752       [
42753         "Saint Barthélemy",
42754         "bl",
42755         "590",
42756         1
42757       ],
42758       [
42759         "Saint Helena",
42760         "sh",
42761         "290"
42762       ],
42763       [
42764         "Saint Kitts and Nevis",
42765         "kn",
42766         "1869"
42767       ],
42768       [
42769         "Saint Lucia",
42770         "lc",
42771         "1758"
42772       ],
42773       [
42774         "Saint Martin (Saint-Martin (partie française))",
42775         "mf",
42776         "590",
42777         2
42778       ],
42779       [
42780         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42781         "pm",
42782         "508"
42783       ],
42784       [
42785         "Saint Vincent and the Grenadines",
42786         "vc",
42787         "1784"
42788       ],
42789       [
42790         "Samoa",
42791         "ws",
42792         "685"
42793       ],
42794       [
42795         "San Marino",
42796         "sm",
42797         "378"
42798       ],
42799       [
42800         "São Tomé and Príncipe (São Tomé e Príncipe)",
42801         "st",
42802         "239"
42803       ],
42804       [
42805         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42806         "sa",
42807         "966"
42808       ],
42809       [
42810         "Senegal (Sénégal)",
42811         "sn",
42812         "221"
42813       ],
42814       [
42815         "Serbia (Србија)",
42816         "rs",
42817         "381"
42818       ],
42819       [
42820         "Seychelles",
42821         "sc",
42822         "248"
42823       ],
42824       [
42825         "Sierra Leone",
42826         "sl",
42827         "232"
42828       ],
42829       [
42830         "Singapore",
42831         "sg",
42832         "65"
42833       ],
42834       [
42835         "Sint Maarten",
42836         "sx",
42837         "1721"
42838       ],
42839       [
42840         "Slovakia (Slovensko)",
42841         "sk",
42842         "421"
42843       ],
42844       [
42845         "Slovenia (Slovenija)",
42846         "si",
42847         "386"
42848       ],
42849       [
42850         "Solomon Islands",
42851         "sb",
42852         "677"
42853       ],
42854       [
42855         "Somalia (Soomaaliya)",
42856         "so",
42857         "252"
42858       ],
42859       [
42860         "South Africa",
42861         "za",
42862         "27"
42863       ],
42864       [
42865         "South Korea (대한민국)",
42866         "kr",
42867         "82"
42868       ],
42869       [
42870         "South Sudan (‫جنوب السودان‬‎)",
42871         "ss",
42872         "211"
42873       ],
42874       [
42875         "Spain (España)",
42876         "es",
42877         "34"
42878       ],
42879       [
42880         "Sri Lanka (ශ්‍රී ලංකාව)",
42881         "lk",
42882         "94"
42883       ],
42884       [
42885         "Sudan (‫السودان‬‎)",
42886         "sd",
42887         "249"
42888       ],
42889       [
42890         "Suriname",
42891         "sr",
42892         "597"
42893       ],
42894       [
42895         "Svalbard and Jan Mayen",
42896         "sj",
42897         "47",
42898         1
42899       ],
42900       [
42901         "Swaziland",
42902         "sz",
42903         "268"
42904       ],
42905       [
42906         "Sweden (Sverige)",
42907         "se",
42908         "46"
42909       ],
42910       [
42911         "Switzerland (Schweiz)",
42912         "ch",
42913         "41"
42914       ],
42915       [
42916         "Syria (‫سوريا‬‎)",
42917         "sy",
42918         "963"
42919       ],
42920       [
42921         "Taiwan (台灣)",
42922         "tw",
42923         "886"
42924       ],
42925       [
42926         "Tajikistan",
42927         "tj",
42928         "992"
42929       ],
42930       [
42931         "Tanzania",
42932         "tz",
42933         "255"
42934       ],
42935       [
42936         "Thailand (ไทย)",
42937         "th",
42938         "66"
42939       ],
42940       [
42941         "Timor-Leste",
42942         "tl",
42943         "670"
42944       ],
42945       [
42946         "Togo",
42947         "tg",
42948         "228"
42949       ],
42950       [
42951         "Tokelau",
42952         "tk",
42953         "690"
42954       ],
42955       [
42956         "Tonga",
42957         "to",
42958         "676"
42959       ],
42960       [
42961         "Trinidad and Tobago",
42962         "tt",
42963         "1868"
42964       ],
42965       [
42966         "Tunisia (‫تونس‬‎)",
42967         "tn",
42968         "216"
42969       ],
42970       [
42971         "Turkey (Türkiye)",
42972         "tr",
42973         "90"
42974       ],
42975       [
42976         "Turkmenistan",
42977         "tm",
42978         "993"
42979       ],
42980       [
42981         "Turks and Caicos Islands",
42982         "tc",
42983         "1649"
42984       ],
42985       [
42986         "Tuvalu",
42987         "tv",
42988         "688"
42989       ],
42990       [
42991         "U.S. Virgin Islands",
42992         "vi",
42993         "1340"
42994       ],
42995       [
42996         "Uganda",
42997         "ug",
42998         "256"
42999       ],
43000       [
43001         "Ukraine (Україна)",
43002         "ua",
43003         "380"
43004       ],
43005       [
43006         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43007         "ae",
43008         "971"
43009       ],
43010       [
43011         "United Kingdom",
43012         "gb",
43013         "44",
43014         0
43015       ],
43016       [
43017         "United States",
43018         "us",
43019         "1",
43020         0
43021       ],
43022       [
43023         "Uruguay",
43024         "uy",
43025         "598"
43026       ],
43027       [
43028         "Uzbekistan (Oʻzbekiston)",
43029         "uz",
43030         "998"
43031       ],
43032       [
43033         "Vanuatu",
43034         "vu",
43035         "678"
43036       ],
43037       [
43038         "Vatican City (Città del Vaticano)",
43039         "va",
43040         "39",
43041         1
43042       ],
43043       [
43044         "Venezuela",
43045         "ve",
43046         "58"
43047       ],
43048       [
43049         "Vietnam (Việt Nam)",
43050         "vn",
43051         "84"
43052       ],
43053       [
43054         "Wallis and Futuna (Wallis-et-Futuna)",
43055         "wf",
43056         "681"
43057       ],
43058       [
43059         "Western Sahara (‫الصحراء الغربية‬‎)",
43060         "eh",
43061         "212",
43062         1
43063       ],
43064       [
43065         "Yemen (‫اليمن‬‎)",
43066         "ye",
43067         "967"
43068       ],
43069       [
43070         "Zambia",
43071         "zm",
43072         "260"
43073       ],
43074       [
43075         "Zimbabwe",
43076         "zw",
43077         "263"
43078       ],
43079       [
43080         "Åland Islands",
43081         "ax",
43082         "358",
43083         1
43084       ]
43085   ];
43086   
43087   return d;
43088 }/**
43089 *    This script refer to:
43090 *    Title: International Telephone Input
43091 *    Author: Jack O'Connor
43092 *    Code version:  v12.1.12
43093 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43094 **/
43095
43096 /**
43097  * @class Roo.bootstrap.PhoneInput
43098  * @extends Roo.bootstrap.TriggerField
43099  * An input with International dial-code selection
43100  
43101  * @cfg {String} defaultDialCode default '+852'
43102  * @cfg {Array} preferedCountries default []
43103   
43104  * @constructor
43105  * Create a new PhoneInput.
43106  * @param {Object} config Configuration options
43107  */
43108
43109 Roo.bootstrap.PhoneInput = function(config) {
43110     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43111 };
43112
43113 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43114         
43115         listWidth: undefined,
43116         
43117         selectedClass: 'active',
43118         
43119         invalidClass : "has-warning",
43120         
43121         validClass: 'has-success',
43122         
43123         allowed: '0123456789',
43124         
43125         max_length: 15,
43126         
43127         /**
43128          * @cfg {String} defaultDialCode The default dial code when initializing the input
43129          */
43130         defaultDialCode: '+852',
43131         
43132         /**
43133          * @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
43134          */
43135         preferedCountries: false,
43136         
43137         getAutoCreate : function()
43138         {
43139             var data = Roo.bootstrap.PhoneInputData();
43140             var align = this.labelAlign || this.parentLabelAlign();
43141             var id = Roo.id();
43142             
43143             this.allCountries = [];
43144             this.dialCodeMapping = [];
43145             
43146             for (var i = 0; i < data.length; i++) {
43147               var c = data[i];
43148               this.allCountries[i] = {
43149                 name: c[0],
43150                 iso2: c[1],
43151                 dialCode: c[2],
43152                 priority: c[3] || 0,
43153                 areaCodes: c[4] || null
43154               };
43155               this.dialCodeMapping[c[2]] = {
43156                   name: c[0],
43157                   iso2: c[1],
43158                   priority: c[3] || 0,
43159                   areaCodes: c[4] || null
43160               };
43161             }
43162             
43163             var cfg = {
43164                 cls: 'form-group',
43165                 cn: []
43166             };
43167             
43168             var input =  {
43169                 tag: 'input',
43170                 id : id,
43171                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43172                 maxlength: this.max_length,
43173                 cls : 'form-control tel-input',
43174                 autocomplete: 'new-password'
43175             };
43176             
43177             var hiddenInput = {
43178                 tag: 'input',
43179                 type: 'hidden',
43180                 cls: 'hidden-tel-input'
43181             };
43182             
43183             if (this.name) {
43184                 hiddenInput.name = this.name;
43185             }
43186             
43187             if (this.disabled) {
43188                 input.disabled = true;
43189             }
43190             
43191             var flag_container = {
43192                 tag: 'div',
43193                 cls: 'flag-box',
43194                 cn: [
43195                     {
43196                         tag: 'div',
43197                         cls: 'flag'
43198                     },
43199                     {
43200                         tag: 'div',
43201                         cls: 'caret'
43202                     }
43203                 ]
43204             };
43205             
43206             var box = {
43207                 tag: 'div',
43208                 cls: this.hasFeedback ? 'has-feedback' : '',
43209                 cn: [
43210                     hiddenInput,
43211                     input,
43212                     {
43213                         tag: 'input',
43214                         cls: 'dial-code-holder',
43215                         disabled: true
43216                     }
43217                 ]
43218             };
43219             
43220             var container = {
43221                 cls: 'roo-select2-container input-group',
43222                 cn: [
43223                     flag_container,
43224                     box
43225                 ]
43226             };
43227             
43228             if (this.fieldLabel.length) {
43229                 var indicator = {
43230                     tag: 'i',
43231                     tooltip: 'This field is required'
43232                 };
43233                 
43234                 var label = {
43235                     tag: 'label',
43236                     'for':  id,
43237                     cls: 'control-label',
43238                     cn: []
43239                 };
43240                 
43241                 var label_text = {
43242                     tag: 'span',
43243                     html: this.fieldLabel
43244                 };
43245                 
43246                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43247                 label.cn = [
43248                     indicator,
43249                     label_text
43250                 ];
43251                 
43252                 if(this.indicatorpos == 'right') {
43253                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43254                     label.cn = [
43255                         label_text,
43256                         indicator
43257                     ];
43258                 }
43259                 
43260                 if(align == 'left') {
43261                     container = {
43262                         tag: 'div',
43263                         cn: [
43264                             container
43265                         ]
43266                     };
43267                     
43268                     if(this.labelWidth > 12){
43269                         label.style = "width: " + this.labelWidth + 'px';
43270                     }
43271                     if(this.labelWidth < 13 && this.labelmd == 0){
43272                         this.labelmd = this.labelWidth;
43273                     }
43274                     if(this.labellg > 0){
43275                         label.cls += ' col-lg-' + this.labellg;
43276                         input.cls += ' col-lg-' + (12 - this.labellg);
43277                     }
43278                     if(this.labelmd > 0){
43279                         label.cls += ' col-md-' + this.labelmd;
43280                         container.cls += ' col-md-' + (12 - this.labelmd);
43281                     }
43282                     if(this.labelsm > 0){
43283                         label.cls += ' col-sm-' + this.labelsm;
43284                         container.cls += ' col-sm-' + (12 - this.labelsm);
43285                     }
43286                     if(this.labelxs > 0){
43287                         label.cls += ' col-xs-' + this.labelxs;
43288                         container.cls += ' col-xs-' + (12 - this.labelxs);
43289                     }
43290                 }
43291             }
43292             
43293             cfg.cn = [
43294                 label,
43295                 container
43296             ];
43297             
43298             var settings = this;
43299             
43300             ['xs','sm','md','lg'].map(function(size){
43301                 if (settings[size]) {
43302                     cfg.cls += ' col-' + size + '-' + settings[size];
43303                 }
43304             });
43305             
43306             this.store = new Roo.data.Store({
43307                 proxy : new Roo.data.MemoryProxy({}),
43308                 reader : new Roo.data.JsonReader({
43309                     fields : [
43310                         {
43311                             'name' : 'name',
43312                             'type' : 'string'
43313                         },
43314                         {
43315                             'name' : 'iso2',
43316                             'type' : 'string'
43317                         },
43318                         {
43319                             'name' : 'dialCode',
43320                             'type' : 'string'
43321                         },
43322                         {
43323                             'name' : 'priority',
43324                             'type' : 'string'
43325                         },
43326                         {
43327                             'name' : 'areaCodes',
43328                             'type' : 'string'
43329                         }
43330                     ]
43331                 })
43332             });
43333             
43334             if(!this.preferedCountries) {
43335                 this.preferedCountries = [
43336                     'hk',
43337                     'gb',
43338                     'us'
43339                 ];
43340             }
43341             
43342             var p = this.preferedCountries.reverse();
43343             
43344             if(p) {
43345                 for (var i = 0; i < p.length; i++) {
43346                     for (var j = 0; j < this.allCountries.length; j++) {
43347                         if(this.allCountries[j].iso2 == p[i]) {
43348                             var t = this.allCountries[j];
43349                             this.allCountries.splice(j,1);
43350                             this.allCountries.unshift(t);
43351                         }
43352                     } 
43353                 }
43354             }
43355             
43356             this.store.proxy.data = {
43357                 success: true,
43358                 data: this.allCountries
43359             };
43360             
43361             return cfg;
43362         },
43363         
43364         initEvents : function()
43365         {
43366             this.createList();
43367             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43368             
43369             this.indicator = this.indicatorEl();
43370             this.flag = this.flagEl();
43371             this.dialCodeHolder = this.dialCodeHolderEl();
43372             
43373             this.trigger = this.el.select('div.flag-box',true).first();
43374             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43375             
43376             var _this = this;
43377             
43378             (function(){
43379                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43380                 _this.list.setWidth(lw);
43381             }).defer(100);
43382             
43383             this.list.on('mouseover', this.onViewOver, this);
43384             this.list.on('mousemove', this.onViewMove, this);
43385             this.inputEl().on("keyup", this.onKeyUp, this);
43386             this.inputEl().on("keypress", this.onKeyPress, this);
43387             
43388             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43389
43390             this.view = new Roo.View(this.list, this.tpl, {
43391                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43392             });
43393             
43394             this.view.on('click', this.onViewClick, this);
43395             this.setValue(this.defaultDialCode);
43396         },
43397         
43398         onTriggerClick : function(e)
43399         {
43400             Roo.log('trigger click');
43401             if(this.disabled){
43402                 return;
43403             }
43404             
43405             if(this.isExpanded()){
43406                 this.collapse();
43407                 this.hasFocus = false;
43408             }else {
43409                 this.store.load({});
43410                 this.hasFocus = true;
43411                 this.expand();
43412             }
43413         },
43414         
43415         isExpanded : function()
43416         {
43417             return this.list.isVisible();
43418         },
43419         
43420         collapse : function()
43421         {
43422             if(!this.isExpanded()){
43423                 return;
43424             }
43425             this.list.hide();
43426             Roo.get(document).un('mousedown', this.collapseIf, this);
43427             Roo.get(document).un('mousewheel', this.collapseIf, this);
43428             this.fireEvent('collapse', this);
43429             this.validate();
43430         },
43431         
43432         expand : function()
43433         {
43434             Roo.log('expand');
43435
43436             if(this.isExpanded() || !this.hasFocus){
43437                 return;
43438             }
43439             
43440             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43441             this.list.setWidth(lw);
43442             
43443             this.list.show();
43444             this.restrictHeight();
43445             
43446             Roo.get(document).on('mousedown', this.collapseIf, this);
43447             Roo.get(document).on('mousewheel', this.collapseIf, this);
43448             
43449             this.fireEvent('expand', this);
43450         },
43451         
43452         restrictHeight : function()
43453         {
43454             this.list.alignTo(this.inputEl(), this.listAlign);
43455             this.list.alignTo(this.inputEl(), this.listAlign);
43456         },
43457         
43458         onViewOver : function(e, t)
43459         {
43460             if(this.inKeyMode){
43461                 return;
43462             }
43463             var item = this.view.findItemFromChild(t);
43464             
43465             if(item){
43466                 var index = this.view.indexOf(item);
43467                 this.select(index, false);
43468             }
43469         },
43470
43471         // private
43472         onViewClick : function(view, doFocus, el, e)
43473         {
43474             var index = this.view.getSelectedIndexes()[0];
43475             
43476             var r = this.store.getAt(index);
43477             
43478             if(r){
43479                 this.onSelect(r, index);
43480             }
43481             if(doFocus !== false && !this.blockFocus){
43482                 this.inputEl().focus();
43483             }
43484         },
43485         
43486         onViewMove : function(e, t)
43487         {
43488             this.inKeyMode = false;
43489         },
43490         
43491         select : function(index, scrollIntoView)
43492         {
43493             this.selectedIndex = index;
43494             this.view.select(index);
43495             if(scrollIntoView !== false){
43496                 var el = this.view.getNode(index);
43497                 if(el){
43498                     this.list.scrollChildIntoView(el, false);
43499                 }
43500             }
43501         },
43502         
43503         createList : function()
43504         {
43505             this.list = Roo.get(document.body).createChild({
43506                 tag: 'ul',
43507                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43508                 style: 'display:none'
43509             });
43510             
43511             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43512         },
43513         
43514         collapseIf : function(e)
43515         {
43516             var in_combo  = e.within(this.el);
43517             var in_list =  e.within(this.list);
43518             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43519             
43520             if (in_combo || in_list || is_list) {
43521                 return;
43522             }
43523             this.collapse();
43524         },
43525         
43526         onSelect : function(record, index)
43527         {
43528             if(this.fireEvent('beforeselect', this, record, index) !== false){
43529                 
43530                 this.setFlagClass(record.data.iso2);
43531                 this.setDialCode(record.data.dialCode);
43532                 this.hasFocus = false;
43533                 this.collapse();
43534                 this.fireEvent('select', this, record, index);
43535             }
43536         },
43537         
43538         flagEl : function()
43539         {
43540             var flag = this.el.select('div.flag',true).first();
43541             if(!flag){
43542                 return false;
43543             }
43544             return flag;
43545         },
43546         
43547         dialCodeHolderEl : function()
43548         {
43549             var d = this.el.select('input.dial-code-holder',true).first();
43550             if(!d){
43551                 return false;
43552             }
43553             return d;
43554         },
43555         
43556         setDialCode : function(v)
43557         {
43558             this.dialCodeHolder.dom.value = '+'+v;
43559         },
43560         
43561         setFlagClass : function(n)
43562         {
43563             this.flag.dom.className = 'flag '+n;
43564         },
43565         
43566         getValue : function()
43567         {
43568             var v = this.inputEl().getValue();
43569             if(this.dialCodeHolder) {
43570                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43571             }
43572             return v;
43573         },
43574         
43575         setValue : function(v)
43576         {
43577             var d = this.getDialCode(v);
43578             
43579             //invalid dial code
43580             if(v.length == 0 || !d || d.length == 0) {
43581                 if(this.rendered){
43582                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43583                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43584                 }
43585                 return;
43586             }
43587             
43588             //valid dial code
43589             this.setFlagClass(this.dialCodeMapping[d].iso2);
43590             this.setDialCode(d);
43591             this.inputEl().dom.value = v.replace('+'+d,'');
43592             this.hiddenEl().dom.value = this.getValue();
43593             
43594             this.validate();
43595         },
43596         
43597         getDialCode : function(v)
43598         {
43599             v = v ||  '';
43600             
43601             if (v.length == 0) {
43602                 return this.dialCodeHolder.dom.value;
43603             }
43604             
43605             var dialCode = "";
43606             if (v.charAt(0) != "+") {
43607                 return false;
43608             }
43609             var numericChars = "";
43610             for (var i = 1; i < v.length; i++) {
43611               var c = v.charAt(i);
43612               if (!isNaN(c)) {
43613                 numericChars += c;
43614                 if (this.dialCodeMapping[numericChars]) {
43615                   dialCode = v.substr(1, i);
43616                 }
43617                 if (numericChars.length == 4) {
43618                   break;
43619                 }
43620               }
43621             }
43622             return dialCode;
43623         },
43624         
43625         reset : function()
43626         {
43627             this.setValue(this.defaultDialCode);
43628             this.validate();
43629         },
43630         
43631         hiddenEl : function()
43632         {
43633             return this.el.select('input.hidden-tel-input',true).first();
43634         },
43635         
43636         // after setting val
43637         onKeyUp : function(e){
43638             this.setValue(this.getValue());
43639         },
43640         
43641         onKeyPress : function(e){
43642             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43643                 e.stopEvent();
43644             }
43645         }
43646         
43647 });
43648 /**
43649  * @class Roo.bootstrap.MoneyField
43650  * @extends Roo.bootstrap.ComboBox
43651  * Bootstrap MoneyField class
43652  * 
43653  * @constructor
43654  * Create a new MoneyField.
43655  * @param {Object} config Configuration options
43656  */
43657
43658 Roo.bootstrap.MoneyField = function(config) {
43659     
43660     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43661     
43662 };
43663
43664 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43665     
43666     /**
43667      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43668      */
43669     allowDecimals : true,
43670     /**
43671      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43672      */
43673     decimalSeparator : ".",
43674     /**
43675      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43676      */
43677     decimalPrecision : 0,
43678     /**
43679      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43680      */
43681     allowNegative : true,
43682     /**
43683      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43684      */
43685     allowZero: true,
43686     /**
43687      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43688      */
43689     minValue : Number.NEGATIVE_INFINITY,
43690     /**
43691      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43692      */
43693     maxValue : Number.MAX_VALUE,
43694     /**
43695      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43696      */
43697     minText : "The minimum value for this field is {0}",
43698     /**
43699      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43700      */
43701     maxText : "The maximum value for this field is {0}",
43702     /**
43703      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43704      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43705      */
43706     nanText : "{0} is not a valid number",
43707     /**
43708      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43709      */
43710     castInt : true,
43711     /**
43712      * @cfg {String} defaults currency of the MoneyField
43713      * value should be in lkey
43714      */
43715     defaultCurrency : false,
43716     /**
43717      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43718      */
43719     thousandsDelimiter : false,
43720     /**
43721      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43722      */
43723     max_length: false,
43724     
43725     inputlg : 9,
43726     inputmd : 9,
43727     inputsm : 9,
43728     inputxs : 6,
43729     
43730     store : false,
43731     
43732     getAutoCreate : function()
43733     {
43734         var align = this.labelAlign || this.parentLabelAlign();
43735         
43736         var id = Roo.id();
43737
43738         var cfg = {
43739             cls: 'form-group',
43740             cn: []
43741         };
43742
43743         var input =  {
43744             tag: 'input',
43745             id : id,
43746             cls : 'form-control roo-money-amount-input',
43747             autocomplete: 'new-password'
43748         };
43749         
43750         var hiddenInput = {
43751             tag: 'input',
43752             type: 'hidden',
43753             id: Roo.id(),
43754             cls: 'hidden-number-input'
43755         };
43756         
43757         if(this.max_length) {
43758             input.maxlength = this.max_length; 
43759         }
43760         
43761         if (this.name) {
43762             hiddenInput.name = this.name;
43763         }
43764
43765         if (this.disabled) {
43766             input.disabled = true;
43767         }
43768
43769         var clg = 12 - this.inputlg;
43770         var cmd = 12 - this.inputmd;
43771         var csm = 12 - this.inputsm;
43772         var cxs = 12 - this.inputxs;
43773         
43774         var container = {
43775             tag : 'div',
43776             cls : 'row roo-money-field',
43777             cn : [
43778                 {
43779                     tag : 'div',
43780                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43781                     cn : [
43782                         {
43783                             tag : 'div',
43784                             cls: 'roo-select2-container input-group',
43785                             cn: [
43786                                 {
43787                                     tag : 'input',
43788                                     cls : 'form-control roo-money-currency-input',
43789                                     autocomplete: 'new-password',
43790                                     readOnly : 1,
43791                                     name : this.currencyName
43792                                 },
43793                                 {
43794                                     tag :'span',
43795                                     cls : 'input-group-addon',
43796                                     cn : [
43797                                         {
43798                                             tag: 'span',
43799                                             cls: 'caret'
43800                                         }
43801                                     ]
43802                                 }
43803                             ]
43804                         }
43805                     ]
43806                 },
43807                 {
43808                     tag : 'div',
43809                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43810                     cn : [
43811                         {
43812                             tag: 'div',
43813                             cls: this.hasFeedback ? 'has-feedback' : '',
43814                             cn: [
43815                                 input
43816                             ]
43817                         }
43818                     ]
43819                 }
43820             ]
43821             
43822         };
43823         
43824         if (this.fieldLabel.length) {
43825             var indicator = {
43826                 tag: 'i',
43827                 tooltip: 'This field is required'
43828             };
43829
43830             var label = {
43831                 tag: 'label',
43832                 'for':  id,
43833                 cls: 'control-label',
43834                 cn: []
43835             };
43836
43837             var label_text = {
43838                 tag: 'span',
43839                 html: this.fieldLabel
43840             };
43841
43842             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43843             label.cn = [
43844                 indicator,
43845                 label_text
43846             ];
43847
43848             if(this.indicatorpos == 'right') {
43849                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43850                 label.cn = [
43851                     label_text,
43852                     indicator
43853                 ];
43854             }
43855
43856             if(align == 'left') {
43857                 container = {
43858                     tag: 'div',
43859                     cn: [
43860                         container
43861                     ]
43862                 };
43863
43864                 if(this.labelWidth > 12){
43865                     label.style = "width: " + this.labelWidth + 'px';
43866                 }
43867                 if(this.labelWidth < 13 && this.labelmd == 0){
43868                     this.labelmd = this.labelWidth;
43869                 }
43870                 if(this.labellg > 0){
43871                     label.cls += ' col-lg-' + this.labellg;
43872                     input.cls += ' col-lg-' + (12 - this.labellg);
43873                 }
43874                 if(this.labelmd > 0){
43875                     label.cls += ' col-md-' + this.labelmd;
43876                     container.cls += ' col-md-' + (12 - this.labelmd);
43877                 }
43878                 if(this.labelsm > 0){
43879                     label.cls += ' col-sm-' + this.labelsm;
43880                     container.cls += ' col-sm-' + (12 - this.labelsm);
43881                 }
43882                 if(this.labelxs > 0){
43883                     label.cls += ' col-xs-' + this.labelxs;
43884                     container.cls += ' col-xs-' + (12 - this.labelxs);
43885                 }
43886             }
43887         }
43888
43889         cfg.cn = [
43890             label,
43891             container,
43892             hiddenInput
43893         ];
43894         
43895         var settings = this;
43896
43897         ['xs','sm','md','lg'].map(function(size){
43898             if (settings[size]) {
43899                 cfg.cls += ' col-' + size + '-' + settings[size];
43900             }
43901         });
43902         
43903         return cfg;
43904     },
43905     
43906     initEvents : function()
43907     {
43908         this.indicator = this.indicatorEl();
43909         
43910         this.initCurrencyEvent();
43911         
43912         this.initNumberEvent();
43913     },
43914     
43915     initCurrencyEvent : function()
43916     {
43917         if (!this.store) {
43918             throw "can not find store for combo";
43919         }
43920         
43921         this.store = Roo.factory(this.store, Roo.data);
43922         this.store.parent = this;
43923         
43924         this.createList();
43925         
43926         this.triggerEl = this.el.select('.input-group-addon', true).first();
43927         
43928         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43929         
43930         var _this = this;
43931         
43932         (function(){
43933             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43934             _this.list.setWidth(lw);
43935         }).defer(100);
43936         
43937         this.list.on('mouseover', this.onViewOver, this);
43938         this.list.on('mousemove', this.onViewMove, this);
43939         this.list.on('scroll', this.onViewScroll, this);
43940         
43941         if(!this.tpl){
43942             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43943         }
43944         
43945         this.view = new Roo.View(this.list, this.tpl, {
43946             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43947         });
43948         
43949         this.view.on('click', this.onViewClick, this);
43950         
43951         this.store.on('beforeload', this.onBeforeLoad, this);
43952         this.store.on('load', this.onLoad, this);
43953         this.store.on('loadexception', this.onLoadException, this);
43954         
43955         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43956             "up" : function(e){
43957                 this.inKeyMode = true;
43958                 this.selectPrev();
43959             },
43960
43961             "down" : function(e){
43962                 if(!this.isExpanded()){
43963                     this.onTriggerClick();
43964                 }else{
43965                     this.inKeyMode = true;
43966                     this.selectNext();
43967                 }
43968             },
43969
43970             "enter" : function(e){
43971                 this.collapse();
43972                 
43973                 if(this.fireEvent("specialkey", this, e)){
43974                     this.onViewClick(false);
43975                 }
43976                 
43977                 return true;
43978             },
43979
43980             "esc" : function(e){
43981                 this.collapse();
43982             },
43983
43984             "tab" : function(e){
43985                 this.collapse();
43986                 
43987                 if(this.fireEvent("specialkey", this, e)){
43988                     this.onViewClick(false);
43989                 }
43990                 
43991                 return true;
43992             },
43993
43994             scope : this,
43995
43996             doRelay : function(foo, bar, hname){
43997                 if(hname == 'down' || this.scope.isExpanded()){
43998                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43999                 }
44000                 return true;
44001             },
44002
44003             forceKeyDown: true
44004         });
44005         
44006         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44007         
44008     },
44009     
44010     initNumberEvent : function(e)
44011     {
44012         this.inputEl().on("keydown" , this.fireKey,  this);
44013         this.inputEl().on("focus", this.onFocus,  this);
44014         this.inputEl().on("blur", this.onBlur,  this);
44015         
44016         this.inputEl().relayEvent('keyup', this);
44017         
44018         if(this.indicator){
44019             this.indicator.addClass('invisible');
44020         }
44021  
44022         this.originalValue = this.getValue();
44023         
44024         if(this.validationEvent == 'keyup'){
44025             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44026             this.inputEl().on('keyup', this.filterValidation, this);
44027         }
44028         else if(this.validationEvent !== false){
44029             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44030         }
44031         
44032         if(this.selectOnFocus){
44033             this.on("focus", this.preFocus, this);
44034             
44035         }
44036         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44037             this.inputEl().on("keypress", this.filterKeys, this);
44038         } else {
44039             this.inputEl().relayEvent('keypress', this);
44040         }
44041         
44042         var allowed = "0123456789";
44043         
44044         if(this.allowDecimals){
44045             allowed += this.decimalSeparator;
44046         }
44047         
44048         if(this.allowNegative){
44049             allowed += "-";
44050         }
44051         
44052         if(this.thousandsDelimiter) {
44053             allowed += ",";
44054         }
44055         
44056         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44057         
44058         var keyPress = function(e){
44059             
44060             var k = e.getKey();
44061             
44062             var c = e.getCharCode();
44063             
44064             if(
44065                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44066                     allowed.indexOf(String.fromCharCode(c)) === -1
44067             ){
44068                 e.stopEvent();
44069                 return;
44070             }
44071             
44072             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44073                 return;
44074             }
44075             
44076             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44077                 e.stopEvent();
44078             }
44079         };
44080         
44081         this.inputEl().on("keypress", keyPress, this);
44082         
44083     },
44084     
44085     onTriggerClick : function(e)
44086     {   
44087         if(this.disabled){
44088             return;
44089         }
44090         
44091         this.page = 0;
44092         this.loadNext = false;
44093         
44094         if(this.isExpanded()){
44095             this.collapse();
44096             return;
44097         }
44098         
44099         this.hasFocus = true;
44100         
44101         if(this.triggerAction == 'all') {
44102             this.doQuery(this.allQuery, true);
44103             return;
44104         }
44105         
44106         this.doQuery(this.getRawValue());
44107     },
44108     
44109     getCurrency : function()
44110     {   
44111         var v = this.currencyEl().getValue();
44112         
44113         return v;
44114     },
44115     
44116     restrictHeight : function()
44117     {
44118         this.list.alignTo(this.currencyEl(), this.listAlign);
44119         this.list.alignTo(this.currencyEl(), this.listAlign);
44120     },
44121     
44122     onViewClick : function(view, doFocus, el, e)
44123     {
44124         var index = this.view.getSelectedIndexes()[0];
44125         
44126         var r = this.store.getAt(index);
44127         
44128         if(r){
44129             this.onSelect(r, index);
44130         }
44131     },
44132     
44133     onSelect : function(record, index){
44134         
44135         if(this.fireEvent('beforeselect', this, record, index) !== false){
44136         
44137             this.setFromCurrencyData(index > -1 ? record.data : false);
44138             
44139             this.collapse();
44140             
44141             this.fireEvent('select', this, record, index);
44142         }
44143     },
44144     
44145     setFromCurrencyData : function(o)
44146     {
44147         var currency = '';
44148         
44149         this.lastCurrency = o;
44150         
44151         if (this.currencyField) {
44152             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44153         } else {
44154             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44155         }
44156         
44157         this.lastSelectionText = currency;
44158         
44159         //setting default currency
44160         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44161             this.setCurrency(this.defaultCurrency);
44162             return;
44163         }
44164         
44165         this.setCurrency(currency);
44166     },
44167     
44168     setFromData : function(o)
44169     {
44170         var c = {};
44171         
44172         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44173         
44174         this.setFromCurrencyData(c);
44175         
44176         var value = '';
44177         
44178         if (this.name) {
44179             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44180         } else {
44181             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44182         }
44183         
44184         this.setValue(value);
44185         
44186     },
44187     
44188     setCurrency : function(v)
44189     {   
44190         this.currencyValue = v;
44191         
44192         if(this.rendered){
44193             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44194             this.validate();
44195         }
44196     },
44197     
44198     setValue : function(v)
44199     {
44200         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44201         
44202         this.value = v;
44203         
44204         if(this.rendered){
44205             
44206             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44207             
44208             this.inputEl().dom.value = (v == '') ? '' :
44209                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44210             
44211             if(!this.allowZero && v === '0') {
44212                 this.hiddenEl().dom.value = '';
44213                 this.inputEl().dom.value = '';
44214             }
44215             
44216             this.validate();
44217         }
44218     },
44219     
44220     getRawValue : function()
44221     {
44222         var v = this.inputEl().getValue();
44223         
44224         return v;
44225     },
44226     
44227     getValue : function()
44228     {
44229         return this.fixPrecision(this.parseValue(this.getRawValue()));
44230     },
44231     
44232     parseValue : function(value)
44233     {
44234         if(this.thousandsDelimiter) {
44235             value += "";
44236             r = new RegExp(",", "g");
44237             value = value.replace(r, "");
44238         }
44239         
44240         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44241         return isNaN(value) ? '' : value;
44242         
44243     },
44244     
44245     fixPrecision : function(value)
44246     {
44247         if(this.thousandsDelimiter) {
44248             value += "";
44249             r = new RegExp(",", "g");
44250             value = value.replace(r, "");
44251         }
44252         
44253         var nan = isNaN(value);
44254         
44255         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44256             return nan ? '' : value;
44257         }
44258         return parseFloat(value).toFixed(this.decimalPrecision);
44259     },
44260     
44261     decimalPrecisionFcn : function(v)
44262     {
44263         return Math.floor(v);
44264     },
44265     
44266     validateValue : function(value)
44267     {
44268         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44269             return false;
44270         }
44271         
44272         var num = this.parseValue(value);
44273         
44274         if(isNaN(num)){
44275             this.markInvalid(String.format(this.nanText, value));
44276             return false;
44277         }
44278         
44279         if(num < this.minValue){
44280             this.markInvalid(String.format(this.minText, this.minValue));
44281             return false;
44282         }
44283         
44284         if(num > this.maxValue){
44285             this.markInvalid(String.format(this.maxText, this.maxValue));
44286             return false;
44287         }
44288         
44289         return true;
44290     },
44291     
44292     validate : function()
44293     {
44294         if(this.disabled || this.allowBlank){
44295             this.markValid();
44296             return true;
44297         }
44298         
44299         var currency = this.getCurrency();
44300         
44301         if(this.validateValue(this.getRawValue()) && currency.length){
44302             this.markValid();
44303             return true;
44304         }
44305         
44306         this.markInvalid();
44307         return false;
44308     },
44309     
44310     getName: function()
44311     {
44312         return this.name;
44313     },
44314     
44315     beforeBlur : function()
44316     {
44317         if(!this.castInt){
44318             return;
44319         }
44320         
44321         var v = this.parseValue(this.getRawValue());
44322         
44323         if(v || v == 0){
44324             this.setValue(v);
44325         }
44326     },
44327     
44328     onBlur : function()
44329     {
44330         this.beforeBlur();
44331         
44332         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44333             //this.el.removeClass(this.focusClass);
44334         }
44335         
44336         this.hasFocus = false;
44337         
44338         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44339             this.validate();
44340         }
44341         
44342         var v = this.getValue();
44343         
44344         if(String(v) !== String(this.startValue)){
44345             this.fireEvent('change', this, v, this.startValue);
44346         }
44347         
44348         this.fireEvent("blur", this);
44349     },
44350     
44351     inputEl : function()
44352     {
44353         return this.el.select('.roo-money-amount-input', true).first();
44354     },
44355     
44356     currencyEl : function()
44357     {
44358         return this.el.select('.roo-money-currency-input', true).first();
44359     },
44360     
44361     hiddenEl : function()
44362     {
44363         return this.el.select('input.hidden-number-input',true).first();
44364     }
44365     
44366 });/**
44367  * @class Roo.bootstrap.BezierSignature
44368  * @extends Roo.bootstrap.Component
44369  * Bootstrap BezierSignature class
44370  * This script refer to:
44371  *    Title: Signature Pad
44372  *    Author: szimek
44373  *    Availability: https://github.com/szimek/signature_pad
44374  *
44375  * @constructor
44376  * Create a new BezierSignature
44377  * @param {Object} config The config object
44378  */
44379
44380 Roo.bootstrap.BezierSignature = function(config){
44381     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44382     this.addEvents({
44383         "resize" : true
44384     });
44385 };
44386
44387 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44388 {
44389      
44390     curve_data: [],
44391     
44392     is_empty: true,
44393     
44394     mouse_btn_down: true,
44395     
44396     /**
44397      * @cfg {int} canvas height
44398      */
44399     canvas_height: '200px',
44400     
44401     /**
44402      * @cfg {float|function} Radius of a single dot.
44403      */ 
44404     dot_size: false,
44405     
44406     /**
44407      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44408      */
44409     min_width: 0.5,
44410     
44411     /**
44412      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44413      */
44414     max_width: 2.5,
44415     
44416     /**
44417      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44418      */
44419     throttle: 16,
44420     
44421     /**
44422      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44423      */
44424     min_distance: 5,
44425     
44426     /**
44427      * @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.
44428      */
44429     bg_color: 'rgba(0, 0, 0, 0)',
44430     
44431     /**
44432      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44433      */
44434     dot_color: 'black',
44435     
44436     /**
44437      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44438      */ 
44439     velocity_filter_weight: 0.7,
44440     
44441     /**
44442      * @cfg {function} Callback when stroke begin. 
44443      */
44444     onBegin: false,
44445     
44446     /**
44447      * @cfg {function} Callback when stroke end.
44448      */
44449     onEnd: false,
44450     
44451     getAutoCreate : function()
44452     {
44453         var cls = 'roo-signature column';
44454         
44455         if(this.cls){
44456             cls += ' ' + this.cls;
44457         }
44458         
44459         var col_sizes = [
44460             'lg',
44461             'md',
44462             'sm',
44463             'xs'
44464         ];
44465         
44466         for(var i = 0; i < col_sizes.length; i++) {
44467             if(this[col_sizes[i]]) {
44468                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44469             }
44470         }
44471         
44472         var cfg = {
44473             tag: 'div',
44474             cls: cls,
44475             cn: [
44476                 {
44477                     tag: 'div',
44478                     cls: 'roo-signature-body',
44479                     cn: [
44480                         {
44481                             tag: 'canvas',
44482                             cls: 'roo-signature-body-canvas',
44483                             height: this.canvas_height,
44484                             width: this.canvas_width
44485                         }
44486                     ]
44487                 },
44488                 {
44489                     tag: 'input',
44490                     type: 'file',
44491                     style: 'display: none'
44492                 }
44493             ]
44494         };
44495         
44496         return cfg;
44497     },
44498     
44499     initEvents: function() 
44500     {
44501         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44502         
44503         var canvas = this.canvasEl();
44504         
44505         // mouse && touch event swapping...
44506         canvas.dom.style.touchAction = 'none';
44507         canvas.dom.style.msTouchAction = 'none';
44508         
44509         this.mouse_btn_down = false;
44510         canvas.on('mousedown', this._handleMouseDown, this);
44511         canvas.on('mousemove', this._handleMouseMove, this);
44512         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44513         
44514         if (window.PointerEvent) {
44515             canvas.on('pointerdown', this._handleMouseDown, this);
44516             canvas.on('pointermove', this._handleMouseMove, this);
44517             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44518         }
44519         
44520         if ('ontouchstart' in window) {
44521             canvas.on('touchstart', this._handleTouchStart, this);
44522             canvas.on('touchmove', this._handleTouchMove, this);
44523             canvas.on('touchend', this._handleTouchEnd, this);
44524         }
44525         
44526         Roo.EventManager.onWindowResize(this.resize, this, true);
44527         
44528         // file input event
44529         this.fileEl().on('change', this.uploadImage, this);
44530         
44531         this.clear();
44532         
44533         this.resize();
44534     },
44535     
44536     resize: function(){
44537         
44538         var canvas = this.canvasEl().dom;
44539         var ctx = this.canvasElCtx();
44540         var img_data = false;
44541         
44542         if(canvas.width > 0) {
44543             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44544         }
44545         // setting canvas width will clean img data
44546         canvas.width = 0;
44547         
44548         var style = window.getComputedStyle ? 
44549             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44550             
44551         var padding_left = parseInt(style.paddingLeft) || 0;
44552         var padding_right = parseInt(style.paddingRight) || 0;
44553         
44554         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44555         
44556         if(img_data) {
44557             ctx.putImageData(img_data, 0, 0);
44558         }
44559     },
44560     
44561     _handleMouseDown: function(e)
44562     {
44563         if (e.browserEvent.which === 1) {
44564             this.mouse_btn_down = true;
44565             this.strokeBegin(e);
44566         }
44567     },
44568     
44569     _handleMouseMove: function (e)
44570     {
44571         if (this.mouse_btn_down) {
44572             this.strokeMoveUpdate(e);
44573         }
44574     },
44575     
44576     _handleMouseUp: function (e)
44577     {
44578         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44579             this.mouse_btn_down = false;
44580             this.strokeEnd(e);
44581         }
44582     },
44583     
44584     _handleTouchStart: function (e) {
44585         
44586         e.preventDefault();
44587         if (e.browserEvent.targetTouches.length === 1) {
44588             // var touch = e.browserEvent.changedTouches[0];
44589             // this.strokeBegin(touch);
44590             
44591              this.strokeBegin(e); // assume e catching the correct xy...
44592         }
44593     },
44594     
44595     _handleTouchMove: function (e) {
44596         e.preventDefault();
44597         // var touch = event.targetTouches[0];
44598         // _this._strokeMoveUpdate(touch);
44599         this.strokeMoveUpdate(e);
44600     },
44601     
44602     _handleTouchEnd: function (e) {
44603         var wasCanvasTouched = e.target === this.canvasEl().dom;
44604         if (wasCanvasTouched) {
44605             e.preventDefault();
44606             // var touch = event.changedTouches[0];
44607             // _this._strokeEnd(touch);
44608             this.strokeEnd(e);
44609         }
44610     },
44611     
44612     reset: function () {
44613         this._lastPoints = [];
44614         this._lastVelocity = 0;
44615         this._lastWidth = (this.min_width + this.max_width) / 2;
44616         this.canvasElCtx().fillStyle = this.dot_color;
44617     },
44618     
44619     strokeMoveUpdate: function(e)
44620     {
44621         this.strokeUpdate(e);
44622         
44623         if (this.throttle) {
44624             this.throttleStroke(this.strokeUpdate, this.throttle);
44625         }
44626         else {
44627             this.strokeUpdate(e);
44628         }
44629     },
44630     
44631     strokeBegin: function(e)
44632     {
44633         var newPointGroup = {
44634             color: this.dot_color,
44635             points: []
44636         };
44637         
44638         if (typeof this.onBegin === 'function') {
44639             this.onBegin(e);
44640         }
44641         
44642         this.curve_data.push(newPointGroup);
44643         this.reset();
44644         this.strokeUpdate(e);
44645     },
44646     
44647     strokeUpdate: function(e)
44648     {
44649         var rect = this.canvasEl().dom.getBoundingClientRect();
44650         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44651         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44652         var lastPoints = lastPointGroup.points;
44653         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44654         var isLastPointTooClose = lastPoint
44655             ? point.distanceTo(lastPoint) <= this.min_distance
44656             : false;
44657         var color = lastPointGroup.color;
44658         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44659             var curve = this.addPoint(point);
44660             if (!lastPoint) {
44661                 this.drawDot({color: color, point: point});
44662             }
44663             else if (curve) {
44664                 this.drawCurve({color: color, curve: curve});
44665             }
44666             lastPoints.push({
44667                 time: point.time,
44668                 x: point.x,
44669                 y: point.y
44670             });
44671         }
44672     },
44673     
44674     strokeEnd: function(e)
44675     {
44676         this.strokeUpdate(e);
44677         if (typeof this.onEnd === 'function') {
44678             this.onEnd(e);
44679         }
44680     },
44681     
44682     addPoint:  function (point) {
44683         var _lastPoints = this._lastPoints;
44684         _lastPoints.push(point);
44685         if (_lastPoints.length > 2) {
44686             if (_lastPoints.length === 3) {
44687                 _lastPoints.unshift(_lastPoints[0]);
44688             }
44689             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44690             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44691             _lastPoints.shift();
44692             return curve;
44693         }
44694         return null;
44695     },
44696     
44697     calculateCurveWidths: function (startPoint, endPoint) {
44698         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44699             (1 - this.velocity_filter_weight) * this._lastVelocity;
44700
44701         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44702         var widths = {
44703             end: newWidth,
44704             start: this._lastWidth
44705         };
44706         
44707         this._lastVelocity = velocity;
44708         this._lastWidth = newWidth;
44709         return widths;
44710     },
44711     
44712     drawDot: function (_a) {
44713         var color = _a.color, point = _a.point;
44714         var ctx = this.canvasElCtx();
44715         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44716         ctx.beginPath();
44717         this.drawCurveSegment(point.x, point.y, width);
44718         ctx.closePath();
44719         ctx.fillStyle = color;
44720         ctx.fill();
44721     },
44722     
44723     drawCurve: function (_a) {
44724         var color = _a.color, curve = _a.curve;
44725         var ctx = this.canvasElCtx();
44726         var widthDelta = curve.endWidth - curve.startWidth;
44727         var drawSteps = Math.floor(curve.length()) * 2;
44728         ctx.beginPath();
44729         ctx.fillStyle = color;
44730         for (var i = 0; i < drawSteps; i += 1) {
44731         var t = i / drawSteps;
44732         var tt = t * t;
44733         var ttt = tt * t;
44734         var u = 1 - t;
44735         var uu = u * u;
44736         var uuu = uu * u;
44737         var x = uuu * curve.startPoint.x;
44738         x += 3 * uu * t * curve.control1.x;
44739         x += 3 * u * tt * curve.control2.x;
44740         x += ttt * curve.endPoint.x;
44741         var y = uuu * curve.startPoint.y;
44742         y += 3 * uu * t * curve.control1.y;
44743         y += 3 * u * tt * curve.control2.y;
44744         y += ttt * curve.endPoint.y;
44745         var width = curve.startWidth + ttt * widthDelta;
44746         this.drawCurveSegment(x, y, width);
44747         }
44748         ctx.closePath();
44749         ctx.fill();
44750     },
44751     
44752     drawCurveSegment: function (x, y, width) {
44753         var ctx = this.canvasElCtx();
44754         ctx.moveTo(x, y);
44755         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44756         this.is_empty = false;
44757     },
44758     
44759     clear: function()
44760     {
44761         var ctx = this.canvasElCtx();
44762         var canvas = this.canvasEl().dom;
44763         ctx.fillStyle = this.bg_color;
44764         ctx.clearRect(0, 0, canvas.width, canvas.height);
44765         ctx.fillRect(0, 0, canvas.width, canvas.height);
44766         this.curve_data = [];
44767         this.reset();
44768         this.is_empty = true;
44769     },
44770     
44771     fileEl: function()
44772     {
44773         return  this.el.select('input',true).first();
44774     },
44775     
44776     canvasEl: function()
44777     {
44778         return this.el.select('canvas',true).first();
44779     },
44780     
44781     canvasElCtx: function()
44782     {
44783         return this.el.select('canvas',true).first().dom.getContext('2d');
44784     },
44785     
44786     getImage: function(type)
44787     {
44788         if(this.is_empty) {
44789             return false;
44790         }
44791         
44792         // encryption ?
44793         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44794     },
44795     
44796     drawFromImage: function(img_src)
44797     {
44798         var img = new Image();
44799         
44800         img.onload = function(){
44801             this.canvasElCtx().drawImage(img, 0, 0);
44802         }.bind(this);
44803         
44804         img.src = img_src;
44805         
44806         this.is_empty = false;
44807     },
44808     
44809     selectImage: function()
44810     {
44811         this.fileEl().dom.click();
44812     },
44813     
44814     uploadImage: function(e)
44815     {
44816         var reader = new FileReader();
44817         
44818         reader.onload = function(e){
44819             var img = new Image();
44820             img.onload = function(){
44821                 this.reset();
44822                 this.canvasElCtx().drawImage(img, 0, 0);
44823             }.bind(this);
44824             img.src = e.target.result;
44825         }.bind(this);
44826         
44827         reader.readAsDataURL(e.target.files[0]);
44828     },
44829     
44830     // Bezier Point Constructor
44831     Point: (function () {
44832         function Point(x, y, time) {
44833             this.x = x;
44834             this.y = y;
44835             this.time = time || Date.now();
44836         }
44837         Point.prototype.distanceTo = function (start) {
44838             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44839         };
44840         Point.prototype.equals = function (other) {
44841             return this.x === other.x && this.y === other.y && this.time === other.time;
44842         };
44843         Point.prototype.velocityFrom = function (start) {
44844             return this.time !== start.time
44845             ? this.distanceTo(start) / (this.time - start.time)
44846             : 0;
44847         };
44848         return Point;
44849     }()),
44850     
44851     
44852     // Bezier Constructor
44853     Bezier: (function () {
44854         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44855             this.startPoint = startPoint;
44856             this.control2 = control2;
44857             this.control1 = control1;
44858             this.endPoint = endPoint;
44859             this.startWidth = startWidth;
44860             this.endWidth = endWidth;
44861         }
44862         Bezier.fromPoints = function (points, widths, scope) {
44863             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44864             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44865             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44866         };
44867         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44868             var dx1 = s1.x - s2.x;
44869             var dy1 = s1.y - s2.y;
44870             var dx2 = s2.x - s3.x;
44871             var dy2 = s2.y - s3.y;
44872             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44873             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44874             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44875             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44876             var dxm = m1.x - m2.x;
44877             var dym = m1.y - m2.y;
44878             var k = l2 / (l1 + l2);
44879             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44880             var tx = s2.x - cm.x;
44881             var ty = s2.y - cm.y;
44882             return {
44883                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44884                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44885             };
44886         };
44887         Bezier.prototype.length = function () {
44888             var steps = 10;
44889             var length = 0;
44890             var px;
44891             var py;
44892             for (var i = 0; i <= steps; i += 1) {
44893                 var t = i / steps;
44894                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44895                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44896                 if (i > 0) {
44897                     var xdiff = cx - px;
44898                     var ydiff = cy - py;
44899                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44900                 }
44901                 px = cx;
44902                 py = cy;
44903             }
44904             return length;
44905         };
44906         Bezier.prototype.point = function (t, start, c1, c2, end) {
44907             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44908             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44909             + (3.0 * c2 * (1.0 - t) * t * t)
44910             + (end * t * t * t);
44911         };
44912         return Bezier;
44913     }()),
44914     
44915     throttleStroke: function(fn, wait) {
44916       if (wait === void 0) { wait = 250; }
44917       var previous = 0;
44918       var timeout = null;
44919       var result;
44920       var storedContext;
44921       var storedArgs;
44922       var later = function () {
44923           previous = Date.now();
44924           timeout = null;
44925           result = fn.apply(storedContext, storedArgs);
44926           if (!timeout) {
44927               storedContext = null;
44928               storedArgs = [];
44929           }
44930       };
44931       return function wrapper() {
44932           var args = [];
44933           for (var _i = 0; _i < arguments.length; _i++) {
44934               args[_i] = arguments[_i];
44935           }
44936           var now = Date.now();
44937           var remaining = wait - (now - previous);
44938           storedContext = this;
44939           storedArgs = args;
44940           if (remaining <= 0 || remaining > wait) {
44941               if (timeout) {
44942                   clearTimeout(timeout);
44943                   timeout = null;
44944               }
44945               previous = now;
44946               result = fn.apply(storedContext, storedArgs);
44947               if (!timeout) {
44948                   storedContext = null;
44949                   storedArgs = [];
44950               }
44951           }
44952           else if (!timeout) {
44953               timeout = window.setTimeout(later, remaining);
44954           }
44955           return result;
44956       };
44957   }
44958   
44959 });
44960
44961  
44962
44963